Changeset 1169 in josm for trunk/src/org
- Timestamp:
- 2008-12-23T15:07:05+01:00 (16 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 256 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/Main.java
r1163 r1169 182 182 183 183 /** 184 * Remove the specified layer from the map. If it is the last layer, 184 * Remove the specified layer from the map. If it is the last layer, 185 185 * remove the map as well. 186 186 */ … … 219 219 220 220 /** 221 * Load all plugins specified in preferences. If the parameter is 221 * Load all plugins specified in preferences. If the parameter is 222 222 * <code>true</code>, all early plugins are loaded (before constructor). 223 223 */ … … 281 281 Main.pref.put("pluginmanager.lastupdate",Long.toString(tim)); 282 282 } else if (d > maxTime) { 283 JOptionPane.showMessageDialog(Main.parent, 284 "<html>" + 283 JOptionPane.showMessageDialog(Main.parent, 284 "<html>" + 285 285 tr("Last plugin update more than {0} days ago.", d) + 286 "<br><em>" + 286 "<br><em>" + 287 287 tr("(You can change the number of days after which this warning appears<br>by setting the config option 'pluginmanager.warntime'.)") + 288 288 "</html>"); … … 512 512 } else if (os.toLowerCase().startsWith("windows")) { 513 513 platform = new PlatformHookWindows(); 514 } else if (os.equals("Linux") || os.equals("Solaris") || 515 os.equals("SunOS") || os.equals("AIX") || 514 } else if (os.equals("Linux") || os.equals("Solaris") || 515 os.equals("SunOS") || os.equals("AIX") || 516 516 os.equals("FreeBSD") || os.equals("NetBSD") || os.equals("OpenBSD")) { 517 517 platform = new PlatformHookUnixoid(); -
trunk/src/org/openstreetmap/josm/actions/AboutAction.java
r1138 r1169 51 51 public class AboutAction extends JosmAction { 52 52 53 54 55 56 53 private static final String version; 54 55 private final static JTextArea revision; 56 private static String time; 57 57 58 58 static { … … 60 60 if(u == null) { 61 61 try { 62 u = new URL("jar:" + Main.class.getProtectionDomain().getCodeSource().getLocation().toString() 62 u = new URL("jar:" + Main.class.getProtectionDomain().getCodeSource().getLocation().toString() 63 63 + "!/META-INF/MANIFEST.MF"); 64 64 } catch (MalformedURLException e) { … … 66 66 } 67 67 } 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 68 revision = loadFile(u); 69 70 Pattern versionPattern = Pattern.compile(".*?(?:Revision|Main-Version): ([0-9]*(?: SVN)?).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL); 71 Matcher match = versionPattern.matcher(revision.getText()); 72 version = match.matches() ? match.group(1) : tr("UNKNOWN"); 73 74 Pattern timePattern = Pattern.compile(".*?(?:Last Changed Date|Main-Date): ([^\n]*).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL); 75 match = timePattern.matcher(revision.getText()); 76 time = match.matches() ? match.group(1) : tr("UNKNOWN"); 77 } 78 79 /** 80 * Return string describing version. 81 * Note that the strinc contains the version number plus an optional suffix of " SVN" to indicate an unofficial development build. 82 * @return version string 83 */ 84 static public String getVersionString() { 85 return version; 86 } 87 87 88 88 /** … … 99 99 return myVersion; 100 100 } 101 101 102 102 /** 103 103 * check whether the version is a development build out of SVN. … … 107 107 return version.endsWith(" SVN"); 108 108 } 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 *of problems (e.g. no internet connection).183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 * @returnAn read-only text area with the content of "resource"201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 109 110 public AboutAction() { 111 super(tr("About"), "about", tr("Display the about screen."), Shortcut.registerShortcut("system:about", tr("About"), KeyEvent.VK_F1, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT), true); 112 } 113 114 public void actionPerformed(ActionEvent e) { 115 JTabbedPane about = new JTabbedPane(); 116 117 JTextArea readme = loadFile(Main.class.getResource("/README")); 118 JTextArea contribution = loadFile(Main.class.getResource("/CONTRIBUTION")); 119 120 JPanel info = new JPanel(new GridBagLayout()); 121 info.add(new JLabel(tr("Java OpenStreetMap Editor Version {0}",version)), GBC.eol().fill(GBC.HORIZONTAL)); 122 info.add(new JLabel(tr("last change at {0}",time)), GBC.eol().fill(GBC.HORIZONTAL)); 123 info.add(new JLabel(tr("Java Version {0}",System.getProperty("java.version"))), GBC.eol().fill(GBC.HORIZONTAL)); 124 info.add(GBC.glue(0,10), GBC.eol()); 125 info.add(new JLabel(tr("Homepage")), GBC.std().insets(0,0,10,0)); 126 info.add(new UrlLabel("http://josm.openstreetmap.de"), GBC.eol().fill(GBC.HORIZONTAL)); 127 info.add(new JLabel(tr("Bug Reports")), GBC.std().insets(0,0,10,0)); 128 info.add(new UrlLabel("http://josm.openstreetmap.de/newticket"), GBC.eol().fill(GBC.HORIZONTAL)); 129 info.add(new JLabel(tr("News about JOSM")), GBC.std().insets(0,0,10,0)); 130 info.add(new UrlLabel("http://www.opengeodata.org/?cat=17"), GBC.eol().fill(GBC.HORIZONTAL)); 131 132 about.addTab(tr("Info"), info); 133 about.addTab(tr("Readme"), createScrollPane(readme)); 134 about.addTab(tr("Revision"), createScrollPane(revision)); 135 about.addTab(tr("Contribution"), createScrollPane(contribution)); 136 137 JPanel pluginTab = new JPanel(new GridBagLayout()); 138 for (final PluginProxy p : Main.plugins) { 139 String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : ""); 140 pluginTab.add(new JLabel(name), GBC.std()); 141 pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); 142 pluginTab.add(new JButton(new AbstractAction(tr("Information")){ 143 public void actionPerformed(ActionEvent event) { 144 StringBuilder b = new StringBuilder(); 145 for (Entry<String,String> e : p.info.attr.entrySet()) { 146 b.append(e.getKey()); 147 b.append(": "); 148 b.append(e.getValue()); 149 b.append("\n"); 150 } 151 JTextArea a = new JTextArea(10,40); 152 a.setEditable(false); 153 a.setText(b.toString()); 154 JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a)); 155 } 156 }), GBC.eol()); 157 JLabel label = new JLabel("<html><i>"+(p.info.description==null?tr("no description available"):p.info.description)+"</i></html>"); 158 label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0)); 159 label.setMaximumSize(new Dimension(450,1000)); 160 pluginTab.add(label, GBC.eop().fill(GBC.HORIZONTAL)); 161 } 162 about.addTab(tr("Plugins"), new JScrollPane(pluginTab)); 163 164 about.setPreferredSize(new Dimension(500,300)); 165 166 JOptionPane.showMessageDialog(Main.parent, about, tr("About JOSM..."), 167 JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo")); 168 } 169 170 private JScrollPane createScrollPane(JTextArea area) { 171 area.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 172 area.setOpaque(false); 173 JScrollPane sp = new JScrollPane(area); 174 sp.setBorder(null); 175 sp.setOpaque(false); 176 return sp; 177 } 178 179 /** 180 * Retrieve the latest JOSM version from the JOSM homepage. 181 * @return An string with the latest version or "UNKNOWN" in case 182 * of problems (e.g. no internet connection). 183 */ 184 public static String checkLatestVersion() { 185 String latest; 186 try { 187 InputStream s = new URL("http://josm.openstreetmap.de/current").openStream(); 188 latest = new BufferedReader(new InputStreamReader(s)).readLine(); 189 s.close(); 190 } catch (IOException x) { 191 x.printStackTrace(); 192 return tr("UNKNOWN"); 193 } 194 return latest; 195 } 196 197 /** 198 * Load the specified resource into an TextArea and return it. 199 * @param resource The resource url to load 200 * @return An read-only text area with the content of "resource" 201 */ 202 private static JTextArea loadFile(URL resource) { 203 JTextArea area = new JTextArea(tr("File could not be found.")); 204 area.setEditable(false); 205 Font font = Font.getFont("monospaced"); 206 if (font != null) 207 area.setFont(font); 208 if (resource == null) 209 return area; 210 BufferedReader in; 211 try { 212 in = new BufferedReader(new InputStreamReader(resource.openStream())); 213 StringBuilder sb = new StringBuilder(); 214 for (String line = in.readLine(); line != null; line = in.readLine()) { 215 sb.append(line); 216 sb.append('\n'); 217 } 218 area.setText(sb.toString()); 219 area.setCaretPosition(0); 220 } catch (IOException e) { 221 e.printStackTrace(); 222 } 223 return area; 224 } 225 225 } -
trunk/src/org/openstreetmap/josm/actions/AddNodeAction.java
r1138 r1169 41 41 42 42 /** 43 * This action displays a dialog where the user can enter a latitude and longitude, 43 * This action displays a dialog where the user can enter a latitude and longitude, 44 44 * and when ok is pressed, a new node is created at the specified position. 45 45 */ … … 47 47 48 48 public AddNodeAction() { 49 50 51 49 super(tr("Add Node"), "addnode", tr("Add a node by entering latitude and longitude."), 50 Shortcut.registerShortcut("addnode", tr("Edit: {0}", tr("Add Node")), KeyEvent.VK_D, Shortcut.GROUP_EDIT, 51 Shortcut.SHIFT_DEFAULT), true); 52 52 } 53 53 54 public void actionPerformed(ActionEvent e) { 54 public void actionPerformed(ActionEvent e) { 55 55 JPanel p = new JPanel(new GridBagLayout()); 56 56 p.add(new JLabel("<html>"+ 57 tr("Enter the coordinates for the new node.") + 58 "<br>" + tr("Use decimal degrees.") + 59 "<br>" + tr("Negative values denote Western/Southern hemisphere.")), 57 tr("Enter the coordinates for the new node.") + 58 "<br>" + tr("Use decimal degrees.") + 59 "<br>" + tr("Negative values denote Western/Southern hemisphere.")), 60 60 GBC.eol()); 61 61 62 62 p.add(new JLabel(tr("Latitude")), GBC.std().insets(0,10,5,0)); 63 63 final JTextField lat = new JTextField(12); … … 65 65 p.add(new JLabel(tr("Longitude")), GBC.std().insets(0,0,5,10)); 66 66 final JTextField lon = new JTextField(12); 67 p.add(lon, GBC.eol().insets(0,0,0,10)); 68 67 p.add(lon, GBC.eol().insets(0,0,0,10)); 68 69 69 Node nnew = null; 70 70 … … 79 79 } catch (Exception ex) { } 80 80 } 81 82 83 84 85 86 81 82 /* Now execute the commands to add the dupicated contents of the paste buffer to the map */ 83 84 Main.main.undoRedo.add(new AddCommand(nnew)); 85 Main.ds.setSelected(nnew); 86 Main.map.mapView.repaint(); 87 87 } 88 88 } -
trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java
r1160 r1169 24 24 /** 25 25 * Aligns all selected nodes within a circle. (Useful for roundabouts) 26 * 26 * 27 27 * @author Matthew Newton 28 28 * @author Petr Dlouhý … … 31 31 32 32 public AlignInCircleAction() { 33 super(tr("Align Nodes in Circle"), "aligncircle", tr("Move the selected nodes into a circle."), 34 Shortcut.registerShortcut("tools:aligncircle", tr("Tool: {0}", tr("Align Nodes in Circle")), 33 super(tr("Align Nodes in Circle"), "aligncircle", tr("Move the selected nodes into a circle."), 34 Shortcut.registerShortcut("tools:aligncircle", tr("Tool: {0}", tr("Align Nodes in Circle")), 35 35 KeyEvent.VK_O, Shortcut.GROUP_EDIT), true); 36 36 } -
trunk/src/org/openstreetmap/josm/actions/AlignInLineAction.java
r1084 r1169 29 29 public final class AlignInLineAction extends JosmAction { 30 30 31 32 33 34 31 public AlignInLineAction() { 32 super(tr("Align Nodes in Line"), "alignline", tr("Move the selected nodes onto a line."), 33 Shortcut.registerShortcut("tools:alignline", tr("Tool: {0}", tr("Align Nodes in Line")), KeyEvent.VK_L, Shortcut.GROUP_EDIT), true); 34 } 35 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 36 /** 37 * The general algorithm here is to find the two selected nodes 38 * that are furthest apart, and then to align all other selected 39 * nodes onto the straight line between these nodes. 40 */ 41 public void actionPerformed(ActionEvent e) { 42 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 43 Collection<Node> nodes = new LinkedList<Node>(); 44 Collection<Node> itnodes = new LinkedList<Node>(); 45 for (OsmPrimitive osm : sel) 46 if (osm instanceof Node) { 47 nodes.add((Node)osm); 48 itnodes.add((Node)osm); 49 } 50 // special case if no single nodes are selected and exactly one way is: 51 // then use the way's nodes 52 if ((nodes.size() == 0) && (sel.size() == 1)) 53 for (OsmPrimitive osm : sel) 54 if (osm instanceof Way) { 55 nodes.addAll(((Way)osm).nodes); 56 itnodes.addAll(((Way)osm).nodes); 57 } 58 if (nodes.size() < 3) { 59 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least three nodes.")); 60 return; 61 } 62 62 63 64 65 63 // Find from the selected nodes two that are the furthest apart. 64 // Let's call them A and B. 65 double distance = 0; 66 66 67 68 67 Node nodea = null; 68 Node nodeb = null; 69 69 70 71 72 73 74 75 76 77 78 79 80 70 for (Node n : nodes) { 71 itnodes.remove(n); 72 for (Node m : itnodes) { 73 double dist = Math.sqrt(n.eastNorth.distance(m.eastNorth)); 74 if (dist > distance) { 75 nodea = n; 76 nodeb = m; 77 distance = dist; 78 } 79 } 80 } 81 81 82 83 84 82 // Remove the nodes A and B from the list of nodes to move 83 nodes.remove(nodea); 84 nodes.remove(nodeb); 85 85 86 87 88 89 90 86 // Find out co-ords of A and B 87 double ax = nodea.eastNorth.east(); 88 double ay = nodea.eastNorth.north(); 89 double bx = nodeb.eastNorth.east(); 90 double by = nodeb.eastNorth.north(); 91 91 92 93 92 // A list of commands to do 93 Collection<Command> cmds = new LinkedList<Command>(); 94 94 95 96 97 98 99 95 // OK, for each node to move, work out where to move it! 96 for (Node n : nodes) { 97 // Get existing co-ords of node to move 98 double nx = n.eastNorth.east(); 99 double ny = n.eastNorth.north(); 100 100 101 102 103 104 105 106 107 108 109 110 111 112 101 if (ax == bx) { 102 // Special case if AB is vertical... 103 nx = ax; 104 } else if (ay == by) { 105 // ...or horizontal 106 ny = ay; 107 } else { 108 // Otherwise calculate position by solving y=mx+c 109 double m1 = (by - ay) / (bx - ax); 110 double c1 = ay - (ax * m1); 111 double m2 = (-1) / m1; 112 double c2 = n.eastNorth.north() - (n.eastNorth.east() * m2); 113 113 114 115 116 114 nx = (c2 - c1) / (m1 - m2); 115 ny = (m1 * nx) + c1; 116 } 117 117 118 119 120 118 // Add the command to move the node to its new position. 119 cmds.add(new MoveCommand(n, nx - n.eastNorth.east(), ny - n.eastNorth.north() )); 120 } 121 121 122 123 124 125 122 // Do it! 123 Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds)); 124 Main.map.repaint(); 125 } 126 126 } -
trunk/src/org/openstreetmap/josm/actions/AutoScaleAction.java
r1084 r1169 47 47 public AutoScaleAction(String mode) { 48 48 super(tr("Zoom to {0}", tr(mode)), "dialogs/autoscale/" + mode, tr("Zoom the view to {0}.", tr(mode)), 49 49 Shortcut.registerShortcut("view:zoom"+mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))), getModeShortcut(mode), Shortcut.GROUP_EDIT), true); 50 50 String modeHelp = Character.toUpperCase(mode.charAt(0)) + mode.substring(1); 51 51 putValue("help", "Action/AutoScale/" + modeHelp); -
trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java
r1084 r1169 49 49 public class CombineWayAction extends JosmAction implements SelectionChangedListener { 50 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 //duplicates.236 237 //if so, do it and remove it from the list of remaining chunks.238 //Rather, rinse, repeat.239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 51 public CombineWayAction() { 52 super(tr("Combine Way"), "combineway", tr("Combine several ways into one."), 53 Shortcut.registerShortcut("tools:combineway", tr("Tool: {0}", tr("Combine Way")), KeyEvent.VK_C, Shortcut.GROUP_EDIT), true); 54 DataSet.selListeners.add(this); 55 } 56 57 public void actionPerformed(ActionEvent event) { 58 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 59 LinkedList<Way> selectedWays = new LinkedList<Way>(); 60 61 for (OsmPrimitive osm : selection) 62 if (osm instanceof Way) 63 selectedWays.add((Way)osm); 64 65 if (selectedWays.size() < 2) { 66 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two ways to combine.")); 67 return; 68 } 69 70 // Check whether all ways have identical relationship membership. More 71 // specifically: If one of the selected ways is a member of relation X 72 // in role Y, then all selected ways must be members of X in role Y. 73 74 // FIXME: In a later revision, we should display some sort of conflict 75 // dialog like we do for tags, to let the user choose which relations 76 // should be kept. 77 78 // Step 1, iterate over all relations and figure out which of our 79 // selected ways are members of a relation. 80 HashMap<Pair<Relation,String>, HashSet<Way>> backlinks = 81 new HashMap<Pair<Relation,String>, HashSet<Way>>(); 82 HashSet<Relation> relationsUsingWays = new HashSet<Relation>(); 83 for (Relation r : Main.ds.relations) { 84 if (r.deleted || r.incomplete) continue; 85 for (RelationMember rm : r.members) { 86 if (rm.member instanceof Way) { 87 for(Way w : selectedWays) { 88 if (rm.member == w) { 89 Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.role); 90 HashSet<Way> waylinks = new HashSet<Way>(); 91 if (backlinks.containsKey(pair)) { 92 waylinks = backlinks.get(pair); 93 } else { 94 waylinks = new HashSet<Way>(); 95 backlinks.put(pair, waylinks); 96 } 97 waylinks.add(w); 98 99 // this is just a cache for later use 100 relationsUsingWays.add(r); 101 } 102 } 103 } 104 } 105 } 106 107 // Complain to the user if the ways don't have equal memberships. 108 for (HashSet<Way> waylinks : backlinks.values()) { 109 if (!waylinks.containsAll(selectedWays)) { 110 int option = JOptionPane.showConfirmDialog(Main.parent, 111 tr("The selected ways have differing relation memberships. " 112 + "Do you still want to combine them?"), 113 tr("Combine ways with different memberships?"), 114 JOptionPane.YES_NO_OPTION); 115 if (option == JOptionPane.YES_OPTION) 116 break; 117 return; 118 } 119 } 120 121 // collect properties for later conflict resolving 122 Map<String, Set<String>> props = new TreeMap<String, Set<String>>(); 123 for (Way w : selectedWays) { 124 for (Entry<String,String> e : w.entrySet()) { 125 if (!props.containsKey(e.getKey())) 126 props.put(e.getKey(), new TreeSet<String>()); 127 props.get(e.getKey()).add(e.getValue()); 128 } 129 } 130 131 List<Node> nodeList = null; 132 Object firstTry = actuallyCombineWays(selectedWays, false); 133 if (firstTry instanceof List) { 134 nodeList = (List<Node>) firstTry; 135 } else { 136 Object secondTry = actuallyCombineWays(selectedWays, true); 137 if (secondTry instanceof List) { 138 int option = JOptionPane.showConfirmDialog(Main.parent, 139 tr("The ways can not be combined in their current directions. " 140 + "Do you want to reverse some of them?"), tr("Change directions?"), 141 JOptionPane.YES_NO_OPTION); 142 if (option != JOptionPane.YES_OPTION) { 143 return; 144 } 145 nodeList = (List<Node>) secondTry; 146 } else { 147 JOptionPane.showMessageDialog(Main.parent, secondTry); 148 return; 149 } 150 } 151 152 // Find the most appropriate way to modify. 153 154 // Eventually this might want to be the way with the longest 155 // history or the longest selected way but for now just attempt 156 // to reuse an existing id. 157 Way modifyWay = selectedWays.peek(); 158 for (Way w : selectedWays) { 159 modifyWay = w; 160 if (w.id != 0) break; 161 } 162 Way newWay = new Way(modifyWay); 163 164 newWay.nodes.clear(); 165 newWay.nodes.addAll(nodeList); 166 167 // display conflict dialog 168 Map<String, JComboBox> components = new HashMap<String, JComboBox>(); 169 JPanel p = new JPanel(new GridBagLayout()); 170 for (Entry<String, Set<String>> e : props.entrySet()) { 171 if (TigerUtils.isTigerTag(e.getKey())) { 172 String combined = TigerUtils.combineTags(e.getKey(), e.getValue()); 173 newWay.put(e.getKey(), combined); 174 } else if (e.getValue().size() > 1) { 175 if("created_by".equals(e.getKey())) 176 { 177 newWay.put("created_by", "JOSM"); 178 } 179 else 180 { 181 JComboBox c = new JComboBox(e.getValue().toArray()); 182 c.setEditable(true); 183 p.add(new JLabel(e.getKey()), GBC.std()); 184 p.add(Box.createHorizontalStrut(10), GBC.std()); 185 p.add(c, GBC.eol()); 186 components.put(e.getKey(), c); 187 } 188 } else 189 newWay.put(e.getKey(), e.getValue().iterator().next()); 190 } 191 192 if (!components.isEmpty()) { 193 int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION); 194 if (answer != JOptionPane.OK_OPTION) 195 return; 196 for (Entry<String, JComboBox> e : components.entrySet()) 197 newWay.put(e.getKey(), e.getValue().getEditor().getItem().toString()); 198 } 199 200 LinkedList<Command> cmds = new LinkedList<Command>(); 201 LinkedList<Way> deletedWays = new LinkedList<Way>(selectedWays); 202 deletedWays.remove(modifyWay); 203 cmds.add(new DeleteCommand(deletedWays)); 204 cmds.add(new ChangeCommand(modifyWay, newWay)); 205 206 // modify all relations containing the now-deleted ways 207 for (Relation r : relationsUsingWays) { 208 Relation newRel = new Relation(r); 209 newRel.members.clear(); 210 HashSet<String> rolesToReAdd = new HashSet<String>(); 211 for (RelationMember rm : r.members) { 212 // Don't copy the member if it to one of our ways, just keep a 213 // note to re-add it later on. 214 if (selectedWays.contains(rm.member)) { 215 rolesToReAdd.add(rm.role); 216 } else { 217 newRel.members.add(rm); 218 } 219 } 220 for (String role : rolesToReAdd) { 221 newRel.members.add(new RelationMember(role, modifyWay)); 222 } 223 cmds.add(new ChangeCommand(r, newRel)); 224 } 225 Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds)); 226 Main.ds.setSelected(modifyWay); 227 } 228 229 /** 230 * @return a message if combining failed, else a list of nodes. 231 */ 232 private Object actuallyCombineWays(List<Way> ways, boolean ignoreDirection) { 233 // Battle plan: 234 // 1. Split the ways into small chunks of 2 nodes and weed out 235 // duplicates. 236 // 2. Take a chunk and see if others could be appended or prepended, 237 // if so, do it and remove it from the list of remaining chunks. 238 // Rather, rinse, repeat. 239 // 3. If this algorithm does not produce a single way, 240 // complain to the user. 241 // 4. Profit! 242 243 HashSet<Pair<Node,Node>> chunkSet = new HashSet<Pair<Node,Node>>(); 244 for (Way w : ways) 245 chunkSet.addAll(w.getNodePairs(ignoreDirection)); 246 247 LinkedList<Pair<Node,Node>> chunks = new LinkedList<Pair<Node,Node>>(chunkSet); 248 249 if (chunks.isEmpty()) { 250 return tr("All the ways were empty"); 251 } 252 253 List<Node> nodeList = Pair.toArrayList(chunks.poll()); 254 while (!chunks.isEmpty()) { 255 ListIterator<Pair<Node,Node>> it = chunks.listIterator(); 256 boolean foundChunk = false; 257 while (it.hasNext()) { 258 Pair<Node,Node> curChunk = it.next(); 259 if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append 260 nodeList.add(curChunk.b); 261 } else if (curChunk.b == nodeList.get(0)) { // prepend 262 nodeList.add(0, curChunk.a); 263 } else if (ignoreDirection && curChunk.b == nodeList.get(nodeList.size() - 1)) { // append 264 nodeList.add(curChunk.a); 265 } else if (ignoreDirection && curChunk.a == nodeList.get(0)) { // prepend 266 nodeList.add(0, curChunk.b); 267 } else { 268 continue; 269 } 270 271 foundChunk = true; 272 it.remove(); 273 break; 274 } 275 if (!foundChunk) break; 276 } 277 278 if (!chunks.isEmpty()) { 279 return tr("Could not combine ways " 280 + "(They could not be merged into a single string of nodes)"); 281 } 282 283 return nodeList; 284 } 285 286 /** 287 * Enable the "Combine way" menu option if more then one way is selected 288 */ 289 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 290 boolean first = false; 291 for (OsmPrimitive osm : newSelection) { 292 if (osm instanceof Way) { 293 if (first) { 294 setEnabled(true); 295 return; 296 } 297 first = true; 298 } 299 } 300 setEnabled(false); 301 } 302 302 } -
trunk/src/org/openstreetmap/josm/actions/CopyAction.java
r1084 r1169 28 28 public final class CopyAction extends JosmAction implements SelectionChangedListener { 29 29 30 30 private LinkedList<JosmAction> listeners; 31 31 32 33 34 35 36 37 38 39 32 public CopyAction() { 33 super(tr("Copy"), "copy", 34 tr("Copy selected objects to paste buffer."), 35 Shortcut.registerShortcut("system:copy", tr("Edit: {0}", tr("Copy")), KeyEvent.VK_C, Shortcut.GROUP_MENU), true); 36 setEnabled(false); 37 DataSet.selListeners.add(this); 38 listeners = new LinkedList<JosmAction>(); 39 } 40 40 41 42 43 41 @Override public void addListener(JosmAction a) { 42 listeners.add(a); 43 } 44 44 45 46 47 48 49 50 51 45 public void actionPerformed(ActionEvent e) { 46 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 47 if (sel.isEmpty()) { 48 JOptionPane.showMessageDialog(Main.parent, 49 tr("Please select something to copy.")); 50 return; 51 } 52 52 53 54 55 56 53 /* New pasteBuffer - will be assigned to the global one at the end */ 54 final DataSet pasteBuffer = new DataSet(); 55 final HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>(); 56 /* temporarily maps old nodes to new so we can do a true deep copy */ 57 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 58 /* scan the selected objects, mapping them to copies; when copying a way or relation, 59 * the copy references the copies of their child objects */ 60 new Visitor(){ 61 public void visit(Node n) { 62 /* check if already in pasteBuffer - e.g. two ways are selected which share a node; 63 * or a way and a node in that way is selected, we'll see it twice, once via the 64 * way and once directly; and so on. */ 65 if (map.containsKey(n)) { return; } 66 Node nnew = new Node(n); 67 map.put(n, nnew); 68 pasteBuffer.addPrimitive(nnew); 69 } 70 public void visit(Way w) { 71 /* check if already in pasteBuffer - could have come from a relation, and directly etc. */ 72 if (map.containsKey(w)) { return; } 73 Way wnew = new Way(); 74 wnew.cloneFrom(w); 75 wnew.nodes.clear(); 76 List<Node> nodes = new ArrayList<Node>(); 77 for (Node n : w.nodes) { 78 if (! map.containsKey(n)) { 79 n.visit(this); 80 } 81 nodes.add((Node)map.get(n)); 82 } 83 wnew.nodes.clear(); 84 wnew.nodes.addAll(nodes); 85 pasteBuffer.addPrimitive(wnew); 86 } 87 public void visit(Relation e) { 88 if (map.containsKey(e)) { return; } 89 Relation enew = new Relation(e); 90 List<RelationMember> members = new ArrayList<RelationMember>(); 91 for (RelationMember m : e.members) { 92 if (! map.containsKey(m.member)) { 93 m.member.visit(this); 94 } 95 RelationMember mnew = new RelationMember(m); 96 mnew.member = map.get(m.member); 97 members.add(mnew); 98 } 99 enew.members.addAll(members); 100 pasteBuffer.addPrimitive(enew); 101 } 102 public void visitAll() { 103 for (OsmPrimitive osm : Main.ds.getSelected()) 104 osm.visit(this); 105 } 106 }.visitAll(); 107 107 108 109 108 Main.pasteBuffer = pasteBuffer; 109 Main.main.menu.paste.setEnabled(true); /* now we have a paste buffer we can make paste available */ 110 110 111 112 113 114 111 for(JosmAction a : listeners) { 112 a.pasteBufferChanged(Main.pasteBuffer); 113 } 114 } 115 115 116 117 118 116 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 117 setEnabled(! newSelection.isEmpty()); 118 } 119 119 } -
trunk/src/org/openstreetmap/josm/actions/CreateCircleAction.java
r1084 r1169 34 34 public final class CreateCircleAction extends JosmAction { 35 35 36 37 38 39 36 public CreateCircleAction() { 37 super(tr("Create Circle"), "createcircle", tr("Create a circle from three selected nodes."), 38 Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 39 } 40 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 41 private double calcang(double xc, double yc, double x, double y) { 42 // calculate the angle from xc|yc to x|y 43 if (xc == x && yc == y) { 44 return 0; // actually invalid, but we won't have this case in this context 45 } 46 double yd = Math.abs(y - yc); 47 if (yd == 0 && xc < x) { 48 return 0; 49 } 50 if (yd == 0 && xc > x) { 51 return Math.PI; 52 } 53 double xd = Math.abs(x - xc); 54 double a = Math.atan2(xd, yd); 55 if (y > yc) { 56 a = Math.PI - a; 57 } 58 if (x < xc) { 59 a = -a; 60 } 61 a = 1.5*Math.PI + a; 62 if (a < 0) { 63 a += 2*Math.PI; 64 } 65 if (a >= 2*Math.PI) { 66 a -= 2*Math.PI; 67 } 68 return a; 69 } 70 70 71 72 73 74 75 76 77 71 public void actionPerformed(ActionEvent e) { 72 int numberOfNodesInCircle = Integer.parseInt(Main.pref.get("createcircle.nodecount", "8")); 73 if (numberOfNodesInCircle < 1) { 74 numberOfNodesInCircle = 1; 75 } else if (numberOfNodesInCircle > 100) { 76 numberOfNodesInCircle = 100; 77 } 78 78 79 80 81 79 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 80 Collection<Node> nodes = new LinkedList<Node>(); 81 Way existingWay = null; 82 82 83 84 85 83 for (OsmPrimitive osm : sel) 84 if (osm instanceof Node) 85 nodes.add((Node)osm); 86 86 87 88 89 90 91 92 93 94 95 96 97 98 87 // special case if no single nodes are selected and exactly one way is: 88 // then use the way's nodes 89 if ((nodes.size() == 0) && (sel.size() == 1)) 90 for (OsmPrimitive osm : sel) 91 if (osm instanceof Way) { 92 existingWay = ((Way)osm); 93 for (Node n : ((Way)osm).nodes) 94 { 95 if(!nodes.contains(n)) 96 nodes.add(n); 97 } 98 } 99 99 100 101 102 103 100 if (nodes.size() != 3) { 101 JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly three nodes or one way with exactly three nodes.")); 102 return; 103 } 104 104 105 106 107 108 109 110 111 112 113 114 105 // let's get some shorter names 106 Node n1 = ((Node)nodes.toArray()[0]); 107 double x1 = n1.eastNorth.east(); 108 double y1 = n1.eastNorth.north(); 109 Node n2 = ((Node)nodes.toArray()[1]); 110 double x2 = n2.eastNorth.east(); 111 double y2 = n2.eastNorth.north(); 112 Node n3 = ((Node)nodes.toArray()[2]); 113 double x3 = n3.eastNorth.east(); 114 double y3 = n3.eastNorth.north(); 115 115 116 117 118 116 // calculate the center (xc/yc) 117 double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1)); 118 double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3); 119 119 120 121 122 123 120 if (sUnder == 0) { 121 JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle.")); 122 return; 123 } 124 124 125 125 s /= sUnder; 126 126 127 128 127 double xc = 0.5*(x1 + x2) + s*(y2 - y1); 128 double yc = 0.5*(y1 + y2) + s*(x1 - x2); 129 129 130 131 130 // calculate the radius (r) 131 double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 132 132 133 134 135 136 137 138 139 133 // find where to put the existing nodes 134 double a1 = calcang(xc, yc, x1, y1); 135 double a2 = calcang(xc, yc, x2, y2); 136 double a3 = calcang(xc, yc, x3, y3); 137 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 138 if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; } 139 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 140 140 141 142 141 // now we can start doing thigs to OSM data 142 Collection<Command> cmds = new LinkedList<Command>(); 143 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 144 // build a way for the circle 145 Way wayToAdd; 146 if (existingWay == null) { 147 wayToAdd = new Way(); 148 } else { 149 // re-use existing way if it was selected 150 wayToAdd = new Way(existingWay); 151 wayToAdd.nodes.clear(); 152 } 153 for (int i = 1; i <= numberOfNodesInCircle; i++) { 154 double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 155 // insert existing nodes if they fit before this new node (999 means "already added this node") 156 if (a1 < 999 && a1 > a) { 157 wayToAdd.nodes.add(n1); 158 a1 = 999; 159 } 160 if (a2 < 999 && a2 > a) { 161 wayToAdd.nodes.add(n2); 162 a2 = 999; 163 } 164 if (a3 < 999 && a3 > a) { 165 wayToAdd.nodes.add(n3); 166 a3 = 999; 167 } 168 // get the position of the new node and insert it 169 double x = xc + r*Math.cos(a); 170 double y = yc + r*Math.sin(a); 171 Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 172 wayToAdd.nodes.add(n); 173 cmds.add(new AddCommand(n)); 174 } 175 wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 176 if (existingWay == null) { 177 cmds.add(new AddCommand(wayToAdd)); 178 } else { 179 cmds.add(new ChangeCommand(existingWay, wayToAdd)); 180 } 181 181 182 183 184 182 Main.main.undoRedo.add(new SequenceCommand(tr("Create Circle"), cmds)); 183 Main.map.repaint(); 184 } 185 185 } -
trunk/src/org/openstreetmap/josm/actions/DeleteAction.java
r1087 r1169 17 17 public final class DeleteAction extends JosmAction implements SelectionChangedListener { 18 18 19 20 21 19 public DeleteAction() { 20 super(tr("Delete"), "dialogs/delete", tr("Delete selected objects."), 21 Shortcut.registerShortcut("system:delete", tr("Edit: {0}", tr("Delete")), KeyEvent.VK_DELETE, Shortcut.GROUP_DIRECT), true); 22 22 DataSet.selListeners.add(this); 23 24 23 setEnabled(false); 24 } 25 25 26 27 28 29 26 public void actionPerformed(ActionEvent e) { 27 new org.openstreetmap.josm.actions.mapmode.DeleteAction(Main.map) 28 .doActionPerformed(e); 29 } 30 30 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 31 31 setEnabled(! newSelection.isEmpty()); -
trunk/src/org/openstreetmap/josm/actions/DiskAccessAction.java
r1084 r1169 17 17 abstract public class DiskAccessAction extends JosmAction { 18 18 19 20 21 19 public DiskAccessAction(String name, String iconName, String tooltip, Shortcut shortcut) { 20 super(name, iconName, tooltip, shortcut, true); 21 } 22 22 23 24 25 26 23 @Deprecated 24 public DiskAccessAction(String name, String iconName, String tooltip, int shortcut, int modifiers) { 25 super(name, iconName, tooltip, shortcut, modifiers, true); 26 } 27 27 28 29 30 31 32 33 34 28 protected static JFileChooser createAndOpenFileChooser(boolean open, boolean multiple, String title) { 29 String curDir = Main.pref.get("lastDirectory"); 30 if (curDir.equals("")) 31 curDir = "."; 32 JFileChooser fc = new JFileChooser(new File(curDir)); 33 if(title != null) 34 fc.setDialogTitle(title); 35 35 36 37 38 39 36 fc.setMultiSelectionEnabled(multiple); 37 for (int i = 0; i < ExtensionFileFilter.filters.length; ++i) 38 fc.addChoosableFileFilter(ExtensionFileFilter.filters[i]); 39 fc.setAcceptAllFileFilterUsed(true); 40 40 41 42 43 41 int answer = open ? fc.showOpenDialog(Main.parent) : fc.showSaveDialog(Main.parent); 42 if (answer != JFileChooser.APPROVE_OPTION) 43 return null; 44 44 45 46 45 if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir)) 46 Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath()); 47 47 48 49 50 51 52 53 48 if (!open) { 49 File file = fc.getSelectedFile(); 50 if (file == null || (file.exists() && JOptionPane.YES_OPTION != 51 JOptionPane.showConfirmDialog(Main.parent, tr("File exists. Overwrite?"), tr("Overwrite"), JOptionPane.YES_NO_OPTION))) 52 return null; 53 } 54 54 55 56 55 return fc; 56 } 57 57 } -
trunk/src/org/openstreetmap/josm/actions/DownloadAction.java
r1084 r1169 28 28 public class DownloadAction extends JosmAction { 29 29 30 30 public DownloadDialog dialog; 31 31 32 33 34 35 32 public DownloadAction() { 33 super(tr("Download from OSM ..."), "download", tr("Download map data from the OSM server."), 34 Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download from OSM ...")), KeyEvent.VK_D, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true); 35 } 36 36 37 38 37 public void actionPerformed(ActionEvent e) { 38 dialog = new DownloadDialog(); 39 39 40 41 40 JPanel downPanel = new JPanel(new GridBagLayout()); 41 downPanel.add(dialog, GBC.eol().fill(GBC.BOTH)); 42 42 43 44 45 46 43 JOptionPane pane = new JOptionPane(downPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); 44 JDialog dlg = pane.createDialog(Main.parent, tr("Download")); 45 dlg.setResizable(true); 46 dialog.setOptionPane(pane); 47 47 48 49 50 51 48 if (dlg.getWidth() > 1000) 49 dlg.setSize(1000, dlg.getHeight()); 50 if (dlg.getHeight() > 600) 51 dlg.setSize(dlg.getWidth(),600); 52 52 53 53 boolean finish = false; 54 54 while (!finish) { 55 55 dlg.setVisible(true); 56 56 Main.pref.put("download.newlayer", dialog.newLayer.isSelected()); 57 58 59 60 61 62 63 64 65 66 67 68 69 57 if (pane.getValue() instanceof Integer && (Integer)pane.getValue() == JOptionPane.OK_OPTION) { 58 Main.pref.put("download.tab", Integer.toString(dialog.getSelectedTab())); 59 for (DownloadTask task : dialog.downloadTasks) { 60 Main.pref.put("download."+task.getPreferencesSuffix(), task.getCheckBox().isSelected()); 61 if (task.getCheckBox().isSelected()) { 62 task.download(this, dialog.minlat, dialog.minlon, dialog.maxlat, dialog.maxlon); 63 finish = true; 64 } 65 } 66 } else 67 finish = true; 68 if (!finish) 69 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least one task to download")); 70 70 } 71 71 72 72 dialog = null; 73 74 73 dlg.dispose(); 74 } 75 75 } -
trunk/src/org/openstreetmap/josm/actions/DuplicateAction.java
r1084 r1169 18 18 19 19 public DuplicateAction() { 20 21 22 23 24 20 super(tr("Duplicate"), "duplicate", 21 tr("Duplicate selection by copy and immediate paste."), 22 Shortcut.registerShortcut("system:duplicate", tr("Edit: {0}", tr("Duplicate")), KeyEvent.VK_D, Shortcut.GROUP_MENU), true); 23 setEnabled(false); 24 DataSet.selListeners.add(this); 25 25 } 26 26 27 28 29 27 public void actionPerformed(ActionEvent e) { 28 Main.main.menu.copy.actionPerformed(e); 29 Main.main.menu.paste.actionPerformed(e); 30 30 } 31 31 32 33 34 32 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 33 setEnabled(! newSelection.isEmpty()); 34 } 35 35 } -
trunk/src/org/openstreetmap/josm/actions/ExitAction.java
r1084 r1169 16 16 */ 17 17 public class ExitAction extends JosmAction { 18 19 20 21 22 23 24 18 /** 19 * Construct the action with "Exit" as label 20 */ 21 public ExitAction() { 22 super(tr("Exit"), "exit", tr("Exit the application."), 23 Shortcut.registerShortcut("system:menuexit", tr("Exit"), KeyEvent.VK_Q, Shortcut.GROUP_MENU), true); 24 } 25 25 26 27 28 29 26 public void actionPerformed(ActionEvent e) { 27 if (!Main.breakBecauseUnsavedChanges()) 28 System.exit(0); 29 } 30 30 } -
trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java
r929 r1169 9 9 10 10 /** 11 * A file filter that filters after the extension. Also includes a list of file 11 * A file filter that filters after the extension. Also includes a list of file 12 12 * filters used in JOSM. 13 * 13 * 14 14 * @author imi 15 15 */ 16 16 public class ExtensionFileFilter extends FileFilter { 17 18 19 17 private final String extension; 18 private final String description; 19 public final String defaultExtension; 20 20 21 public static final int OSM = 0; 22 public static final int GPX = 1; 23 public static final int NMEA = 2; 24 25 public static ExtensionFileFilter[] filters = { 26 new ExtensionFileFilter("osm,xml", "osm", tr("OSM Server Files (*.osm *.xml)")), 27 new ExtensionFileFilter("gpx,gpx.gz", "gpx", tr("GPX Files (*.gpx *.gpx.gz)")), 28 new ExtensionFileFilter("nmea,txt", "nmea", tr("NMEA-0183 Files (*.nmea *.txt)")), 29 }; 21 public static final int OSM = 0; 22 public static final int GPX = 1; 23 public static final int NMEA = 2; 30 24 31 /** 32 * Construct an extension file filter by giving the extension to check after. 33 * 34 */ 35 public ExtensionFileFilter(String extension, String defExt, String description) { 36 this.extension = extension; 37 defaultExtension = defExt; 38 this.description = description; 39 } 25 public static ExtensionFileFilter[] filters = { 26 new ExtensionFileFilter("osm,xml", "osm", tr("OSM Server Files (*.osm *.xml)")), 27 new ExtensionFileFilter("gpx,gpx.gz", "gpx", tr("GPX Files (*.gpx *.gpx.gz)")), 28 new ExtensionFileFilter("nmea,txt", "nmea", tr("NMEA-0183 Files (*.nmea *.txt)")), 29 }; 40 30 41 public boolean acceptName(String filename) { 42 String name = filename.toLowerCase(); 43 for (String ext : extension.split(",")) 44 if (name.endsWith("."+ext)) 45 return true; 46 return false; 47 } 31 /** 32 * Construct an extension file filter by giving the extension to check after. 33 * 34 */ 35 public ExtensionFileFilter(String extension, String defExt, String description) { 36 this.extension = extension; 37 defaultExtension = defExt; 38 this.description = description; 39 } 48 40 49 @Override public boolean accept(File pathname) { 50 if (pathname.isDirectory()) 51 return true; 52 return acceptName(pathname.getName()); 53 } 41 public boolean acceptName(String filename) { 42 String name = filename.toLowerCase(); 43 for (String ext : extension.split(",")) 44 if (name.endsWith("."+ext)) 45 return true; 46 return false; 47 } 54 48 55 @Override public String getDescription() { 56 return description; 57 } 49 @Override public boolean accept(File pathname) { 50 if (pathname.isDirectory()) 51 return true; 52 return acceptName(pathname.getName()); 53 } 54 55 @Override public String getDescription() { 56 return description; 57 } 58 58 } -
trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java
r1084 r1169 40 40 public class GpxExportAction extends DiskAccessAction { 41 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 42 private final static String warningGpl = "<html><font color='red' size='-2'>"+tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.")+"</html>"; 43 44 private final Layer layer; 45 46 public GpxExportAction(Layer layer) { 47 super(tr("Export to GPX ..."), "exportgpx", tr("Export the data to GPX file."), 48 Shortcut.registerShortcut("file:exportgpx", tr("Export to GPX ..."), KeyEvent.VK_E, Shortcut.GROUP_MENU)); 49 this.layer = layer; 50 } 51 52 public void actionPerformed(ActionEvent e) { 53 if (layer == null && Main.map == null) { 54 JOptionPane.showMessageDialog(Main.parent, tr("Nothing to export. Get some data first.")); 55 return; 56 } 57 58 JFileChooser fc = createAndOpenFileChooser(false, false, null); 59 if (fc == null) 60 return; 61 File file = fc.getSelectedFile(); 62 if (file == null) 63 return; 64 65 exportGpx(file, this.layer == null ? Main.main.editLayer() : this.layer); 66 } 67 68 public static void exportGpx(File file, Layer layer) { 69 String fn = file.getPath(); 70 if (fn.indexOf('.') == -1) { 71 fn += ".gpx"; 72 file = new File(fn); 73 } 74 75 // open the dialog asking for options 76 JPanel p = new JPanel(new GridBagLayout()); 77 78 p.add(new JLabel(tr("gps track description")), GBC.eol()); 79 JTextArea desc = new JTextArea(3,40); 80 desc.setWrapStyleWord(true); 81 desc.setLineWrap(true); 82 p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH)); 83 84 JCheckBox author = new JCheckBox(tr("Add author information"), Main.pref.getBoolean("lastAddAuthor", true)); 85 author.setSelected(true); 86 p.add(author, GBC.eol()); 87 JLabel nameLabel = new JLabel(tr("Real name")); 88 p.add(nameLabel, GBC.std().insets(10,0,5,0)); 89 JTextField authorName = new JTextField(Main.pref.get("lastAuthorName")); 90 p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL)); 91 JLabel emailLabel = new JLabel(tr("Email")); 92 p.add(emailLabel, GBC.std().insets(10,0,5,0)); 93 JTextField email = new JTextField(Main.pref.get("osm-server.username")); 94 p.add(email, GBC.eol().fill(GBC.HORIZONTAL)); 95 JLabel copyrightLabel = new JLabel(tr("Copyright (URL)")); 96 p.add(copyrightLabel, GBC.std().insets(10,0,5,0)); 97 JTextField copyright = new JTextField(); 98 p.add(copyright, GBC.std().fill(GBC.HORIZONTAL)); 99 JButton predefined = new JButton(tr("Predefined")); 100 p.add(predefined, GBC.eol().insets(5,0,0,0)); 101 JLabel copyrightYearLabel = new JLabel(tr("Copyright year")); 102 p.add(copyrightYearLabel, GBC.std().insets(10,0,5,5)); 103 JTextField copyrightYear = new JTextField(""); 104 p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL)); 105 JLabel warning = new JLabel("<html><font size='-2'> </html"); 106 p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15,0,0,0)); 107 addDependencies(author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel, copyrightLabel, copyrightYearLabel, warning); 108 109 p.add(new JLabel(tr("Keywords")), GBC.eol()); 110 JTextField keywords = new JTextField(); 111 p.add(keywords, GBC.eop().fill(GBC.HORIZONTAL)); 112 113 int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Export options"), JOptionPane.OK_CANCEL_OPTION); 114 if (answer != JOptionPane.OK_OPTION) 115 return; 116 117 Main.pref.put("lastAddAuthor", author.isSelected()); 118 if (authorName.getText().length() != 0) 119 Main.pref.put("lastAuthorName", authorName.getText()); 120 if (copyright.getText().length() != 0) 121 Main.pref.put("lastCopyright", copyright.getText()); 122 123 GpxData gpxData; 124 if (layer instanceof OsmDataLayer) 125 gpxData = ((OsmDataLayer)layer).toGpxData(); 126 else if (layer instanceof GpxLayer) 127 gpxData = ((GpxLayer)layer).data; 128 else 129 gpxData = OsmDataLayer.toGpxData(Main.ds); 130 // TODO: add copyright, etc. 131 try { 132 FileOutputStream fo = new FileOutputStream(file); 133 new GpxWriter(fo).write(gpxData); 134 fo.flush(); 135 fo.close(); 136 } catch (IOException x) { 137 x.printStackTrace(); 138 JOptionPane.showMessageDialog(Main.parent, tr("Error while exporting {0}", fn)+":\n"+x.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE); 139 } 140 } 141 142 /** 143 * Add all those listeners to handle the enable state of the fields. 144 * @param copyrightYearLabel 145 * @param copyrightLabel 146 * @param emailLabel 147 * @param nameLabel 148 * @param warning 149 */ 150 private static void addDependencies( 151 final JCheckBox author, 152 final JTextField authorName, 153 final JTextField email, 154 final JTextField copyright, 155 final JButton predefined, 156 final JTextField copyrightYear, 157 final JLabel nameLabel, 158 final JLabel emailLabel, 159 final JLabel copyrightLabel, 160 final JLabel copyrightYearLabel, 161 final JLabel warning) { 162 163 ActionListener authorActionListener = new ActionListener(){ 164 public void actionPerformed(ActionEvent e) { 165 boolean b = author.isSelected(); 166 authorName.setEnabled(b); 167 email.setEnabled(b); 168 nameLabel.setEnabled(b); 169 emailLabel.setEnabled(b); 170 authorName.setText(b ? Main.pref.get("lastAuthorName") : ""); 171 email.setText(b ? Main.pref.get("osm-server.username") : ""); 172 173 boolean authorSet = authorName.getText().length() != 0; 174 enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b && authorSet); 175 } 176 }; 177 author.addActionListener(authorActionListener); 178 179 KeyAdapter authorNameListener = new KeyAdapter(){ 180 @Override public void keyReleased(KeyEvent e) { 181 boolean b = authorName.getText().length()!=0 && author.isSelected(); 182 enableCopyright(copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b); 183 } 184 }; 185 authorName.addKeyListener(authorNameListener); 186 187 predefined.addActionListener(new ActionListener(){ 188 public void actionPerformed(ActionEvent e) { 189 JList l = new JList(new String[]{"Creative Commons By-SA", "public domain", "GNU Lesser Public License (LGPL)", "BSD License (MIT/X11)"}); 190 l.setVisibleRowCount(4); 191 l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 192 int answer = JOptionPane.showConfirmDialog(Main.parent, new JScrollPane(l),tr("Choose a predefined license"), JOptionPane.OK_CANCEL_OPTION); 193 if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1) 194 return; 195 final String[] urls = { 196 "http://creativecommons.org/licenses/by-sa/2.5", 197 "public domain", 198 "http://www.gnu.org/copyleft/lesser.html", 199 "http://www.opensource.org/licenses/bsd-license.php"}; 200 String license = ""; 201 for (int i : l.getSelectedIndices()) { 202 if (i == 1) { 203 license = "public domain"; 204 break; 205 } 206 license += license.length()==0 ? urls[i] : ", "+urls[i]; 207 } 208 copyright.setText(license); 209 copyright.setCaretPosition(0); 210 } 211 }); 212 213 authorActionListener.actionPerformed(null); 214 authorNameListener.keyReleased(null); 215 } 216 217 private static void enableCopyright(final JTextField copyright, final JButton predefined, final JTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel, final JLabel warning, boolean enable) { 218 copyright.setEnabled(enable); 219 predefined.setEnabled(enable); 220 copyrightYear.setEnabled(enable); 221 copyrightLabel.setEnabled(enable); 222 copyrightYearLabel.setEnabled(enable); 223 warning.setText(enable ? warningGpl : "<html><font size='-2'> </html"); 224 225 if (enable && copyrightYear.getText().length()==0) { 226 copyrightYear.setText(enable ? Integer.toString(Calendar.getInstance().get(Calendar.YEAR)) : ""); 227 } else if (!enable) 228 copyrightYear.setText(""); 229 230 if (enable && copyright.getText().length()==0) { 231 copyright.setText(enable ? Main.pref.get("lastCopyright", "http://creativecommons.org/licenses/by-sa/2.5") : ""); 232 copyright.setCaretPosition(0); 233 } else if (!enable) 234 copyright.setText(""); 235 } 236 236 } -
trunk/src/org/openstreetmap/josm/actions/HelpAction.java
r1163 r1169 42 42 public class HelpAction extends AbstractAction { 43 43 44 45 44 public interface Helpful { 45 String helpTopic(); 46 46 } 47 47 48 48 private String languageCode = Main.getLanguageCodeU(); 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 49 private JFrame helpBrowser = new JFrame(tr("JOSM Online Help")); 50 private String baseurl = Main.pref.get("help.baseurl", "http://josm.openstreetmap.de"); 51 private JEditorPane help = new JEditorPane(); 52 private WikiReader reader = new WikiReader(baseurl); 53 private String url; 54 55 public HelpAction() { 56 super(tr("Help"), ImageProvider.get("help")); 57 help.setEditable(false); 58 help.addHyperlinkListener(new HyperlinkListener(){ 59 public void hyperlinkUpdate(HyperlinkEvent e) { 60 if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) 61 return; 62 if (e.getURL() == null) 63 help.setText("<html>404 not found</html>"); 64 else if (e.getURL().toString().startsWith(WikiReader.JOSM_EXTERN)) 65 OpenBrowser.displayUrl("http://"+e.getURL().toString().substring(WikiReader.JOSM_EXTERN.length())+"?action=edit"); 66 else 67 setHelpUrl(e.getURL().toString()); 68 } 69 }); 70 help.setContentType("text/html"); 71 72 JPanel p = new JPanel(new BorderLayout()); 73 helpBrowser.setContentPane(p); 74 75 p.add(new JScrollPane(help), BorderLayout.CENTER); 76 String[] bounds = Main.pref.get("help.window.bounds", "0,0,800,600").split(","); 77 helpBrowser.setBounds( 78 Integer.parseInt(bounds[0]), 79 Integer.parseInt(bounds[1]), 80 Integer.parseInt(bounds[2]), 81 Integer.parseInt(bounds[3])); 82 83 JPanel buttons = new JPanel(); 84 p.add(buttons, BorderLayout.SOUTH); 85 createButton(buttons, tr("Open in Browser")); 86 createButton(buttons, tr("Edit")); 87 createButton(buttons, tr("Reload")); 88 89 helpBrowser.addWindowListener(new WindowAdapter(){ 90 @Override public void windowClosing(WindowEvent e) { 91 closeHelp(); 92 } 93 }); 94 94 95 95 help.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Close"); 96 96 help.getActionMap().put("Close", new AbstractAction(){ 97 98 97 public void actionPerformed(ActionEvent e) { 98 closeHelp(); 99 99 } 100 100 }); 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 } 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 101 } 102 103 public void actionPerformed(ActionEvent e) { 104 if (tr("Open in Browser").equals(e.getActionCommand())) { 105 OpenBrowser.displayUrl(url); 106 } else if (tr("Edit").equals(e.getActionCommand())) { 107 if (!url.startsWith(baseurl)) { 108 JOptionPane.showMessageDialog(Main.parent, tr("Can only edit help pages from JOSM Online Help")); 109 return; 110 } 111 OpenBrowser.displayUrl(url+"?action=edit"); 112 } else if (tr("Reload").equals(e.getActionCommand())) { 113 setHelpUrl(url); 114 } else if (e.getActionCommand() == null) { 115 String topic = null; 116 Point mouse = Main.parent.getMousePosition(); 117 if (mouse != null) 118 topic = contextSensitiveHelp(SwingUtilities.getDeepestComponentAt(Main.parent, mouse.x, mouse.y)); 119 if (topic == null) { 120 helpBrowser.setVisible(false); 121 setHelpUrl(baseurl+"/wiki/Help"); 122 } else 123 help(topic); 124 } else { 125 helpBrowser.setVisible(false); 126 setHelpUrl(baseurl+"/wiki/Help"); 127 } 128 } 129 130 /** 131 * @return The topic of the help. <code>null</code> for "don't know" 132 */ 133 private String contextSensitiveHelp(Object c) { 134 if (c instanceof Helpful) 135 return ((Helpful)c).helpTopic(); 136 if (c instanceof JMenu) 137 return "Menu/"+((JMenu)c).getText(); 138 if (c instanceof AbstractButton) { 139 AbstractButton b = (AbstractButton)c; 140 if (b.getClientProperty("help") != null) 141 return (String)b.getClientProperty("help"); 142 return contextSensitiveHelp(((AbstractButton)c).getAction()); 143 } 144 if (c instanceof Action) 145 return (String)((Action)c).getValue("help"); 146 if (c instanceof Component) 147 return contextSensitiveHelp(((Component)c).getParent()); 148 return null; 149 } 150 151 /** 152 * Displays the help (or browse on the already open help) on the online page 153 * with the given help topic. Use this for larger help descriptions. 154 */ 155 public void help(String topic) { 156 helpBrowser.setVisible(false); 157 setHelpUrl(baseurl+"/wiki/Help/"+topic); 158 } 159 160 /** 161 * Set the content of the help window to a specific text (in html format) 162 * @param url The url this content is the representation of 163 */ 164 public void setHelpUrl(String url) { 165 int i = url.lastIndexOf("/")+1; 166 String title = url.substring(i); 167 if(!title.startsWith(languageCode)) 168 title = languageCode + title; 169 String langurl = url.substring(0, i) + title; 170 if(langurl.equals(this.url) || langurl.equals(url)) 171 { 172 this.url = url; 173 try { 174 help.read(new StringReader(reader.read(url)), help.getEditorKit().createDefaultDocument()); 175 } catch (IOException ex) { 176 help.setText(tr("Error while loading page {0}",url)); 177 } 178 } 179 else 180 { 181 try { 182 help.read(new StringReader(reader.read(langurl)), help.getEditorKit().createDefaultDocument()); 183 String message = help.getText(); 184 String le = "http://josm-extern." + langurl.substring(7); 185 if(message.indexOf("Describe "") >= 0 && message.indexOf(le) >= 0) 186 throw new IOException(); 187 this.url = langurl; 188 } catch (IOException e) { 189 this.url = url; 190 try { 191 help.read(new StringReader(reader.read(url)), help.getEditorKit().createDefaultDocument()); 192 } catch (IOException ex) { 193 help.setText(tr("Error while loading page {0}",url)); 194 } 195 } 196 } 197 helpBrowser.setVisible(true); 198 } 199 200 /** 201 * Closes the help window 202 */ 203 public void closeHelp() { 204 String bounds = helpBrowser.getX()+","+helpBrowser.getY()+","+helpBrowser.getWidth()+","+helpBrowser.getHeight(); 205 Main.pref.put("help.window.bounds", bounds); 206 helpBrowser.setVisible(false); 207 } 208 209 private void createButton(JPanel buttons, String name) { 210 JButton b = new JButton(tr(name)); 211 b.setActionCommand(name); 212 b.addActionListener(this); 213 buttons.add(b); 214 } 215 215 } -
trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java
r1084 r1169 18 18 public class HistoryInfoAction extends JosmAction { 19 19 20 21 22 23 20 public HistoryInfoAction() { 21 super(tr("OSM History Information"), "about",tr("Display history information about OSM ways or nodes."), 22 Shortcut.registerShortcut("core:history", tr("OSM History Information"), KeyEvent.VK_H, Shortcut.GROUP_HOTKEY), true); 23 } 24 24 25 25 public void actionPerformed(ActionEvent e) { 26 26 new Visitor() { 27 27 public void visit(Node n) { 28 29 28 OpenBrowser.displayUrl("http://www.openstreetmap.org/browse/node/" + n.id + "/history"); 29 } 30 30 31 31 public void visit(Way w) { … … 43 43 }.visitAll(); 44 44 45 45 } 46 46 47 47 } -
trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java
r1084 r1169 26 26 27 27 public class JoinNodeWayAction extends JosmAction { 28 29 30 31 28 public JoinNodeWayAction() { 29 super(tr("Join node to way"), "joinnodeway", tr("Join a node into the nearest way segments"), 30 Shortcut.registerShortcut("tools:joinnodeway", tr("Tool: {0}", tr("Join node to way")), KeyEvent.VK_J, Shortcut.GROUP_EDIT), true); 31 } 32 32 33 34 35 36 33 public void actionPerformed(ActionEvent e) { 34 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 35 if (sel.size() != 1 || !(sel.iterator().next() instanceof Node)) return; 36 Node node = (Node) sel.iterator().next(); 37 37 38 39 40 41 42 43 44 45 46 47 48 38 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments( 39 Main.map.mapView.getPoint(node.eastNorth)); 40 HashMap<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>(); 41 for (WaySegment ws : wss) { 42 List<Integer> is; 43 if (insertPoints.containsKey(ws.way)) { 44 is = insertPoints.get(ws.way); 45 } else { 46 is = new ArrayList<Integer>(); 47 insertPoints.put(ws.way, is); 48 } 49 49 50 51 52 53 54 50 if (ws.way.nodes.get(ws.lowerIndex) != node 51 && ws.way.nodes.get(ws.lowerIndex+1) != node) { 52 is.add(ws.lowerIndex); 53 } 54 } 55 55 56 57 58 59 60 61 62 63 64 56 Collection<Command> cmds = new LinkedList<Command>(); 57 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) { 58 Way w = insertPoint.getKey(); 59 Way wnew = new Way(w); 60 List<Integer> is = insertPoint.getValue(); 61 pruneSuccsAndReverse(is); 62 for (int i : is) wnew.nodes.add(i+1, node); 63 cmds.add(new ChangeCommand(w, wnew)); 64 } 65 65 66 67 68 66 Main.main.undoRedo.add(new SequenceCommand(tr("Join Node and Line"), cmds)); 67 Main.map.repaint(); 68 } 69 69 70 71 70 private static void pruneSuccsAndReverse(List<Integer> is) { 71 //if (is.size() < 2) return; 72 72 73 74 75 76 77 78 79 80 81 82 83 73 HashSet<Integer> is2 = new HashSet<Integer>(); 74 for (int i : is) { 75 if (!is2.contains(i - 1) && !is2.contains(i + 1)) { 76 is2.add(i); 77 } 78 } 79 is.clear(); 80 is.addAll(is2); 81 Collections.sort(is); 82 Collections.reverse(is); 83 } 84 84 } -
trunk/src/org/openstreetmap/josm/actions/JosmAction.java
r1084 r1169 25 25 abstract public class JosmAction extends AbstractAction implements Destroyable { 26 26 27 28 29 27 @Deprecated 28 public KeyStroke shortcut; 29 protected Shortcut sc; 30 30 31 32 33 34 35 36 37 38 31 public Shortcut getShortcut() { 32 if (sc == null) { 33 sc = Shortcut.registerShortcut("core:none", "No Shortcut", 0, Shortcut.GROUP_NONE); 34 sc.setAutomatic(); // as this shortcut is shared by all action that don't want to have a shortcut, 35 // we shouldn't allow the user to change it... 36 } 37 return sc; 38 } 39 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 40 @Deprecated 41 public JosmAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean register) { 42 super(name, iconName == null ? null : ImageProvider.get(iconName)); 43 setHelpId(); 44 if (shortcut != 0) { 45 int group = Shortcut.GROUP_LAYER; //GROUP_NONE; 46 if (((modifier & InputEvent.CTRL_MASK) != 0) || ((modifier & InputEvent.CTRL_DOWN_MASK) != 0)) { 47 group = Shortcut.GROUP_MENU; 48 } else if (modifier == 0) { 49 group = Shortcut.GROUP_EDIT; 50 } 51 sc = Shortcut.registerShortcut("auto:"+name, name, shortcut, group); 52 this.shortcut = sc.getKeyStroke(); 53 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), name); 54 Main.contentPane.getActionMap().put(name, this); 55 } 56 putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc)); 57 putValue("toolbar", iconName); 58 58 if (register) 59 60 59 Main.toolbar.register(this); 60 } 61 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 62 /** 63 * The new super for all actions. 64 * 65 * Use this super constructor to setup your action. It takes 5 parameters: 66 * 67 * name - the action's text as displayed on the menu (if it is added to a menu) 68 * iconName - the filename of the icon to use 69 * tooltip - a longer description of the action that will be displayed in the tooltip. Please note 70 * that html is not supported for menu action on some platforms 71 * shortcut - a ready-created shortcut object or null if you don't want a shortcut. But you always 72 * do want a shortcut, remember you can alway register it with group=none, so you 73 * won't be assigned a shurtcut unless the user configures one. If you pass null here, 74 * the user CANNOT configure a shortcut for your action. 75 * register - register this action for the toolbar preferences? 76 */ 77 public JosmAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean register) { 78 super(name, iconName == null ? null : ImageProvider.get(iconName)); 79 setHelpId(); 80 sc = shortcut; 81 if (sc != null) { 82 this.shortcut = sc.getKeyStroke(); 83 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sc.getKeyStroke(), name); 84 Main.contentPane.getActionMap().put(name, this); 85 } 86 putValue(SHORT_DESCRIPTION, Main.platform.makeTooltip(tooltip, sc)); 87 putValue("toolbar", iconName); 88 88 if (register) 89 90 89 Main.toolbar.register(this); 90 } 91 91 92 93 94 95 96 97 92 public void destroy() { 93 if (shortcut != null) { 94 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(sc.getKeyStroke()); 95 Main.contentPane.getActionMap().remove(sc.getKeyStroke()); 96 } 97 } 98 98 99 100 101 99 public JosmAction() { 100 setHelpId(); 101 } 102 102 103 104 105 106 107 108 103 /** 104 * needs to be overridden to be useful 105 */ 106 public void pasteBufferChanged(DataSet newPasteBuffer) { 107 return; 108 } 109 109 110 111 112 113 114 115 110 /** 111 * needs to be overridden to be useful 112 */ 113 public void addListener(JosmAction a) { 114 return; 115 } 116 116 117 118 119 120 121 122 117 private void setHelpId() { 118 String helpId = "Action/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1); 119 if (helpId.endsWith("Action")) 120 helpId = helpId.substring(0, helpId.length()-6); 121 putValue("help", helpId); 122 } 123 123 } -
trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java
r1084 r1169 52 52 public class MergeNodesAction extends JosmAction implements SelectionChangedListener { 53 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 54 public MergeNodesAction() { 55 super(tr("Merge Nodes"), "mergenodes", tr("Merge nodes into the oldest one."), 56 Shortcut.registerShortcut("tools:mergenodes", tr("Tool: {0}", tr("Merge Nodes")), KeyEvent.VK_M, Shortcut.GROUP_EDIT), true); 57 DataSet.selListeners.add(this); 58 } 59 60 public void actionPerformed(ActionEvent event) { 61 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 62 LinkedList<Node> selectedNodes = new LinkedList<Node>(); 63 64 // the selection check should stop this procedure starting if 65 // nothing but node are selected - otherwise we don't care 66 // anyway as long as we have at least two nodes 67 for (OsmPrimitive osm : selection) 68 if (osm instanceof Node) 69 selectedNodes.add((Node)osm); 70 71 if (selectedNodes.size() < 2) { 72 JOptionPane.showMessageDialog(Main.parent, tr("Please select at least two nodes to merge.")); 73 return; 74 } 75 76 // Find which node to merge into (i.e. which one will be left) 77 // - this should be combined from two things: 78 // 1. It will be the first node in the list that has a 79 // positive ID number, OR the first node. 80 // 2. It will be at the position of the first node in the 81 // list. 82 // 83 // *However* - there is the problem that the selection list is 84 // _not_ in the order that the nodes were clicked on, meaning 85 // that the user doesn't know which node will be chosen (so 86 // (2) is not implemented yet.) :-( 87 Node useNode = null; 88 for (Node n: selectedNodes) { 89 if (n.id > 0) { 90 useNode = n; 91 break; 92 } 93 } 94 if (useNode == null) 95 useNode = selectedNodes.iterator().next(); 96 97 mergeNodes(selectedNodes, useNode); 98 } 99 100 /** 101 * really do the merging - returns the node that is left 102 */ 103 public static Node mergeNodes(LinkedList<Node> allNodes, Node dest) { 104 Node newNode = new Node(dest); 105 106 // Check whether all ways have identical relationship membership. More 107 // specifically: If one of the selected ways is a member of relation X 108 // in role Y, then all selected ways must be members of X in role Y. 109 110 // FIXME: In a later revision, we should display some sort of conflict 111 // dialog like we do for tags, to let the user choose which relations 112 // should be kept. 113 114 // Step 1, iterate over all relations and figure out which of our 115 // selected ways are members of a relation. 116 HashMap<Pair<Relation,String>, HashSet<Node>> backlinks = 117 new HashMap<Pair<Relation,String>, HashSet<Node>>(); 118 HashSet<Relation> relationsUsingNodes = new HashSet<Relation>(); 119 for (Relation r : Main.ds.relations) { 120 if (r.deleted || r.incomplete) continue; 121 for (RelationMember rm : r.members) { 122 if (rm.member instanceof Node) { 123 for (Node n : allNodes) { 124 if (rm.member == n) { 125 Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.role); 126 HashSet<Node> nodelinks = new HashSet<Node>(); 127 if (backlinks.containsKey(pair)) { 128 nodelinks = backlinks.get(pair); 129 } else { 130 nodelinks = new HashSet<Node>(); 131 backlinks.put(pair, nodelinks); 132 } 133 nodelinks.add(n); 134 135 // this is just a cache for later use 136 relationsUsingNodes.add(r); 137 } 138 } 139 } 140 } 141 } 142 143 // Complain to the user if the ways don't have equal memberships. 144 for (HashSet<Node> nodelinks : backlinks.values()) { 145 if (!nodelinks.containsAll(allNodes)) { 146 int option = JOptionPane.showConfirmDialog(Main.parent, 147 tr("The selected nodes have differing relation memberships. " 148 + "Do you still want to merge them?"), 149 tr("Merge nodes with different memberships?"), 150 JOptionPane.YES_NO_OPTION); 151 if (option == JOptionPane.YES_OPTION) 152 break; 153 return null; 154 } 155 } 156 157 // collect properties for later conflict resolving 158 Map<String, Set<String>> props = new TreeMap<String, Set<String>>(); 159 for (Node n : allNodes) { 160 for (Entry<String,String> e : n.entrySet()) { 161 if (!props.containsKey(e.getKey())) 162 props.put(e.getKey(), new TreeSet<String>()); 163 props.get(e.getKey()).add(e.getValue()); 164 } 165 } 166 167 // display conflict dialog 168 Map<String, JComboBox> components = new HashMap<String, JComboBox>(); 169 JPanel p = new JPanel(new GridBagLayout()); 170 for (Entry<String, Set<String>> e : props.entrySet()) { 171 if (TigerUtils.isTigerTag(e.getKey())) { 172 String combined = TigerUtils.combineTags(e.getKey(), e.getValue()); 173 newNode.put(e.getKey(), combined); 174 } else if (e.getValue().size() > 1) { 175 if("created_by".equals(e.getKey())) 176 { 177 newNode.put("created_by", "JOSM"); 178 } 179 else 180 { 181 JComboBox c = new JComboBox(e.getValue().toArray()); 182 c.setEditable(true); 183 p.add(new JLabel(e.getKey()), GBC.std()); 184 p.add(Box.createHorizontalStrut(10), GBC.std()); 185 p.add(c, GBC.eol()); 186 components.put(e.getKey(), c); 187 } 188 } else 189 newNode.put(e.getKey(), e.getValue().iterator().next()); 190 } 191 192 if (!components.isEmpty()) { 193 int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter values for all conflicts."), JOptionPane.OK_CANCEL_OPTION); 194 if (answer != JOptionPane.OK_OPTION) 195 return null; 196 for (Entry<String, JComboBox> e : components.entrySet()) 197 newNode.put(e.getKey(), e.getValue().getEditor().getItem().toString()); 198 } 199 200 LinkedList<Command> cmds = new LinkedList<Command>(); 201 cmds.add(new ChangeCommand(dest, newNode)); 202 203 Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>(); 204 205 for (Way w : Main.ds.ways) { 206 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue; 207 boolean modify = false; 208 for (Node sn : allNodes) { 209 if (sn == dest) continue; 210 if (w.nodes.contains(sn)) { 211 modify = true; 212 } 213 } 214 if (!modify) continue; 215 // OK - this way contains one or more nodes to change 216 ArrayList<Node> nn = new ArrayList<Node>(); 217 Node lastNode = null; 218 for (int i = 0; i < w.nodes.size(); i++) { 219 Node pushNode = w.nodes.get(i); 220 if (allNodes.contains(pushNode)) { 221 pushNode = dest; 222 } 223 if (pushNode != lastNode) { 224 nn.add(pushNode); 225 } 226 lastNode = pushNode; 227 } 228 if (nn.size() < 2) { 229 CollectBackReferencesVisitor backRefs = 230 new CollectBackReferencesVisitor(Main.ds, false); 231 w.visit(backRefs); 232 if (!backRefs.data.isEmpty()) { 233 JOptionPane.showMessageDialog(Main.parent, 234 tr("Cannot merge nodes: " + 235 "Would have to delete a way that is still used.")); 236 return null; 237 } 238 del.add(w); 239 } else { 240 Way newWay = new Way(w); 241 newWay.nodes.clear(); 242 newWay.nodes.addAll(nn); 243 cmds.add(new ChangeCommand(w, newWay)); 244 } 245 } 246 247 // delete any merged nodes 248 del.addAll(allNodes); 249 del.remove(dest); 250 if (!del.isEmpty()) cmds.add(new DeleteCommand(del)); 251 252 // modify all relations containing the now-deleted nodes 253 for (Relation r : relationsUsingNodes) { 254 Relation newRel = new Relation(r); 255 newRel.members.clear(); 256 HashSet<String> rolesToReAdd = new HashSet<String>(); 257 for (RelationMember rm : r.members) { 258 // Don't copy the member if it points to one of our nodes, 259 // just keep a note to re-add it later on. 260 if (allNodes.contains(rm.member)) { 261 rolesToReAdd.add(rm.role); 262 } else { 263 newRel.members.add(rm); 264 } 265 } 266 for (String role : rolesToReAdd) { 267 newRel.members.add(new RelationMember(role, dest)); 268 } 269 cmds.add(new ChangeCommand(r, newRel)); 270 } 271 272 Main.main.undoRedo.add(new SequenceCommand(tr("Merge {0} nodes", allNodes.size()), cmds)); 273 Main.ds.setSelected(dest); 274 275 return dest; 276 } 277 278 279 /** 280 * Enable the "Merge Nodes" menu option if more then one node is selected 281 */ 282 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 283 boolean ok = true; 284 if (newSelection.size() < 2) { 285 setEnabled(false); 286 return; 287 } 288 for (OsmPrimitive osm : newSelection) { 289 if (!(osm instanceof Node)) { 290 ok = false; 291 break; 292 } 293 } 294 setEnabled(ok); 295 } 296 296 } -
trunk/src/org/openstreetmap/josm/actions/MoveAction.java
r1084 r1169 26 26 public class MoveAction extends JosmAction { 27 27 28 29 28 public enum Direction { UP, LEFT, RIGHT, DOWN } 29 private Direction myDirection; 30 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 31 // any better idea? 32 private static Object calltosupermustbefirststatementinconstructor(Direction dir, boolean text) { 33 Shortcut sc; 34 String directiontext; 35 if (dir == Direction.UP) { 36 directiontext = tr("up"); 37 sc = Shortcut.registerShortcut("core:moveup", tr("Move objects {0}", directiontext), KeyEvent.VK_UP, Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT); 38 } else if (dir == Direction.DOWN) { 39 directiontext = tr("down"); 40 sc = Shortcut.registerShortcut("core:movedown", tr("Move objects {0}", directiontext), KeyEvent.VK_DOWN, Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT); 41 } else if (dir == Direction.LEFT) { 42 directiontext = tr("left"); 43 sc = Shortcut.registerShortcut("core:moveleft", tr("Move objects {0}", directiontext), KeyEvent.VK_LEFT, Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT); 44 } else { //dir == Direction.RIGHT) { 45 directiontext = tr("right"); 46 sc = Shortcut.registerShortcut("core:moveright", tr("Move objects {0}", directiontext), KeyEvent.VK_RIGHT, Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT); 47 } 48 if (text) { 49 return directiontext; 50 } else { 51 return sc; 52 } 53 } 54 54 55 56 57 58 59 60 55 public MoveAction(Direction dir) { 56 super(tr("Move {0}", calltosupermustbefirststatementinconstructor(dir, true)), null, 57 tr("Moves Objects {0}", calltosupermustbefirststatementinconstructor(dir, true)), 58 (Shortcut)calltosupermustbefirststatementinconstructor(dir, false), true); 59 myDirection = dir; 60 } 61 61 62 62 public void actionPerformed(ActionEvent event) { 63 63 64 65 64 // find out how many "real" units the objects have to be moved in order to 65 // achive an 1-pixel movement 66 66 67 68 67 EastNorth en1 = Main.map.mapView.getEastNorth(100, 100); 68 EastNorth en2 = Main.map.mapView.getEastNorth(101, 101); 69 69 70 71 70 double distx = en2.east() - en1.east(); 71 double disty = en2.north() - en1.north(); 72 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 73 switch (myDirection) { 74 case UP: 75 distx = 0; 76 disty = -disty; 77 break; 78 case DOWN: 79 distx = 0; 80 break; 81 case LEFT: 82 disty = 0; 83 distx = -distx; 84 default: 85 disty = 0; 86 } 87 87 88 89 88 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 89 Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection); 90 90 91 92 91 Command c = !Main.main.undoRedo.commands.isEmpty() 92 ? Main.main.undoRedo.commands.getLast() : null; 93 93 94 95 96 97 98 94 if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand)c).objects)) 95 ((MoveCommand)c).moveAgain(distx, disty); 96 else 97 Main.main.undoRedo.add( 98 c = new MoveCommand(selection, distx, disty)); 99 99 100 101 102 103 104 105 106 107 108 100 for (Node n : affectedNodes) { 101 if (n.coor.isOutSideWorld()) { 102 // Revert move 103 ((MoveCommand) c).moveAgain(-distx, -disty); 104 JOptionPane.showMessageDialog(Main.parent, 105 tr("Cannot move objects outside of the world.")); 106 return; 107 } 108 } 109 109 110 111 110 Main.map.mapView.repaint(); 111 } 112 112 } -
trunk/src/org/openstreetmap/josm/actions/NewAction.java
r1084 r1169 14 14 public class NewAction extends JosmAction { 15 15 16 17 18 19 16 public NewAction() { 17 super(tr("New"), "new", tr("Create a new map."), 18 Shortcut.registerShortcut("system:new", tr("File: {0}", tr("New")), KeyEvent.VK_N, Shortcut.GROUP_MENU), true); 19 } 20 20 21 22 23 21 public void actionPerformed(ActionEvent e) { 22 Main.main.addLayer(new OsmDataLayer(new DataSet(), tr("unnamed"), null)); 23 } 24 24 } -
trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java
r1167 r1169 35 35 public class OpenFileAction extends DiskAccessAction { 36 36 37 /** 38 * Create an open action. The name is "Open a file". 39 */ 40 public OpenFileAction() { 41 super(tr("Open ..."), "open", tr("Open a file."), 42 Shortcut.registerShortcut("system:open", tr("File: {0}", tr("Open ...")), KeyEvent.VK_O, Shortcut.GROUP_MENU)); 43 } 44 45 public void actionPerformed(ActionEvent e) { 46 JFileChooser fc = createAndOpenFileChooser(true, true, null); 47 if (fc == null) 48 return; 49 File[] files = fc.getSelectedFiles(); 50 for (int i = files.length; i > 0; --i) 51 openFile(files[i-1]); 52 } 53 54 /** 55 * Open the given file. 56 */ 57 public void openFile(File file) { 58 try { 59 if (asGpxData(file.getName())) 60 openFileAsGpx(file); 61 else if (asNmeaData(file.getName())) 62 openFileAsNmea(file); 63 else 64 openAsData(file); 65 } catch (SAXException x) { 66 x.printStackTrace(); 67 JOptionPane.showMessageDialog(Main.parent, tr("Error while parsing {0}",file.getName())+": "+x.getMessage()); 68 } catch (IOException x) { 69 x.printStackTrace(); 70 JOptionPane.showMessageDialog(Main.parent, tr("Could not read \"{0}\"",file.getName())+"\n"+x.getMessage()); 71 } 72 } 73 74 private void openAsData(File file) throws SAXException, IOException, FileNotFoundException { 75 String fn = file.getName(); 76 if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(fn)) { 77 DataSet dataSet = OsmReader.parseDataSet(new FileInputStream(file), null, Main.pleaseWaitDlg); 78 OsmDataLayer layer = new OsmDataLayer(dataSet, file.getName(), file); 79 Main.main.addLayer(layer); 80 layer.fireDataChange(); 81 } 82 else 83 JOptionPane.showMessageDialog(Main.parent, fn+": "+tr("Unknown file extension: {0}", fn.substring(file.getName().lastIndexOf('.')+1))); 84 } 85 86 private void openFileAsGpx(File file) throws SAXException, IOException, FileNotFoundException { 87 String fn = file.getName(); 88 if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn)) { 89 GpxReader r = null; 90 InputStream is; 91 if (file.getName().endsWith(".gpx.gz")) { 92 is = new GZIPInputStream(new FileInputStream(file)); 93 } else { 94 is = new FileInputStream(file); 95 } 96 // Workaround for SAX BOM bug 97 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6206835 98 if(!((is.read()==0xef)&&(is.read()==0xbb)&&(is.read()==0xbf))) { 99 is.close(); 100 if (file.getName().endsWith(".gpx.gz")) { 101 is = new GZIPInputStream(new FileInputStream(file)); 102 } else { 103 is = new FileInputStream(file); 104 } 105 } 106 r = new GpxReader(is,file.getAbsoluteFile().getParentFile()); 107 r.data.storageFile = file; 108 GpxLayer gpxLayer = new GpxLayer(r.data, fn); 109 Main.main.addLayer(gpxLayer); 110 if (Main.pref.getBoolean("marker.makeautomarkers", true)) { 111 MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer); 112 if (ml.data.size() > 0) { 113 Main.main.addLayer(ml); 114 } 115 } 116 117 } else { 118 throw new IllegalStateException(); 119 } 120 } 121 122 private void showNmeaInfobox(boolean success, NmeaReader r) { 123 String msg = tr("Coordinates imported: ") + r.getNumberOfCoordinates() + "\n" + 124 tr("Malformed sentences: ") + r.getParserMalformed() + "\n" + 125 tr("Checksum errors: ") + r.getParserChecksumErrors() + "\n"; 126 if(!success) // don't scare the user unneccessarily 127 msg += tr("Unknown sentences: ") + r.getParserUnknown() + "\n"; 128 msg += tr("Zero coordinates: ") + r.getParserZeroCoordinates(); 129 if(success) { 130 JOptionPane.showMessageDialog( 131 Main.parent, msg, 132 tr("NMEA import success"),JOptionPane.INFORMATION_MESSAGE); 133 } else { 134 JOptionPane.showMessageDialog( 135 Main.parent, msg, 136 tr("NMEA import faliure!"),JOptionPane.ERROR_MESSAGE); 137 } 138 } 139 140 private void openFileAsNmea(File file) throws IOException, FileNotFoundException { 141 String fn = file.getName(); 142 if (ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn)) { 143 NmeaReader r = new NmeaReader(new FileInputStream(file), file.getAbsoluteFile().getParentFile()); 144 if(r.getNumberOfCoordinates()>0) { 145 r.data.storageFile = file; 146 GpxLayer gpxLayer = new GpxLayer(r.data, fn); 147 Main.main.addLayer(gpxLayer); 148 if (Main.pref.getBoolean("marker.makeautomarkers", true)) { 149 MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer); 150 if (ml.data.size() > 0) { 151 Main.main.addLayer(ml); 152 } 153 } 154 } 155 showNmeaInfobox(r.getNumberOfCoordinates()>0, r); 156 } else { 157 throw new IllegalStateException(); 158 } 37 /** 38 * Create an open action. The name is "Open a file". 39 */ 40 public OpenFileAction() { 41 super(tr("Open ..."), "open", tr("Open a file."), 42 Shortcut.registerShortcut("system:open", tr("File: {0}", tr("Open ...")), KeyEvent.VK_O, Shortcut.GROUP_MENU)); 159 43 } 160 44 161 private boolean asGpxData(String fn) { 162 return ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn); 163 } 45 public void actionPerformed(ActionEvent e) { 46 JFileChooser fc = createAndOpenFileChooser(true, true, null); 47 if (fc == null) 48 return; 49 File[] files = fc.getSelectedFiles(); 50 for (int i = files.length; i > 0; --i) 51 openFile(files[i-1]); 52 } 164 53 165 private boolean asNmeaData(String fn) { 166 return ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn); 167 } 54 /** 55 * Open the given file. 56 */ 57 public void openFile(File file) { 58 try { 59 if (asGpxData(file.getName())) 60 openFileAsGpx(file); 61 else if (asNmeaData(file.getName())) 62 openFileAsNmea(file); 63 else 64 openAsData(file); 65 } catch (SAXException x) { 66 x.printStackTrace(); 67 JOptionPane.showMessageDialog(Main.parent, tr("Error while parsing {0}",file.getName())+": "+x.getMessage()); 68 } catch (IOException x) { 69 x.printStackTrace(); 70 JOptionPane.showMessageDialog(Main.parent, tr("Could not read \"{0}\"",file.getName())+"\n"+x.getMessage()); 71 } 72 } 73 74 private void openAsData(File file) throws SAXException, IOException, FileNotFoundException { 75 String fn = file.getName(); 76 if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(fn)) { 77 DataSet dataSet = OsmReader.parseDataSet(new FileInputStream(file), null, Main.pleaseWaitDlg); 78 OsmDataLayer layer = new OsmDataLayer(dataSet, file.getName(), file); 79 Main.main.addLayer(layer); 80 layer.fireDataChange(); 81 } 82 else 83 JOptionPane.showMessageDialog(Main.parent, fn+": "+tr("Unknown file extension: {0}", fn.substring(file.getName().lastIndexOf('.')+1))); 84 } 85 86 private void openFileAsGpx(File file) throws SAXException, IOException, FileNotFoundException { 87 String fn = file.getName(); 88 if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn)) { 89 GpxReader r = null; 90 InputStream is; 91 if (file.getName().endsWith(".gpx.gz")) { 92 is = new GZIPInputStream(new FileInputStream(file)); 93 } else { 94 is = new FileInputStream(file); 95 } 96 // Workaround for SAX BOM bug 97 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6206835 98 if(!((is.read()==0xef)&&(is.read()==0xbb)&&(is.read()==0xbf))) { 99 is.close(); 100 if (file.getName().endsWith(".gpx.gz")) { 101 is = new GZIPInputStream(new FileInputStream(file)); 102 } else { 103 is = new FileInputStream(file); 104 } 105 } 106 r = new GpxReader(is,file.getAbsoluteFile().getParentFile()); 107 r.data.storageFile = file; 108 GpxLayer gpxLayer = new GpxLayer(r.data, fn); 109 Main.main.addLayer(gpxLayer); 110 if (Main.pref.getBoolean("marker.makeautomarkers", true)) { 111 MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer); 112 if (ml.data.size() > 0) { 113 Main.main.addLayer(ml); 114 } 115 } 116 117 } else { 118 throw new IllegalStateException(); 119 } 120 } 121 122 private void showNmeaInfobox(boolean success, NmeaReader r) { 123 String msg = tr("Coordinates imported: ") + r.getNumberOfCoordinates() + "\n" + 124 tr("Malformed sentences: ") + r.getParserMalformed() + "\n" + 125 tr("Checksum errors: ") + r.getParserChecksumErrors() + "\n"; 126 if(!success) // don't scare the user unneccessarily 127 msg += tr("Unknown sentences: ") + r.getParserUnknown() + "\n"; 128 msg += tr("Zero coordinates: ") + r.getParserZeroCoordinates(); 129 if(success) { 130 JOptionPane.showMessageDialog( 131 Main.parent, msg, 132 tr("NMEA import success"),JOptionPane.INFORMATION_MESSAGE); 133 } else { 134 JOptionPane.showMessageDialog( 135 Main.parent, msg, 136 tr("NMEA import faliure!"),JOptionPane.ERROR_MESSAGE); 137 } 138 } 139 140 private void openFileAsNmea(File file) throws IOException, FileNotFoundException { 141 String fn = file.getName(); 142 if (ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn)) { 143 NmeaReader r = new NmeaReader(new FileInputStream(file), file.getAbsoluteFile().getParentFile()); 144 if(r.getNumberOfCoordinates()>0) { 145 r.data.storageFile = file; 146 GpxLayer gpxLayer = new GpxLayer(r.data, fn); 147 Main.main.addLayer(gpxLayer); 148 if (Main.pref.getBoolean("marker.makeautomarkers", true)) { 149 MarkerLayer ml = new MarkerLayer(r.data, tr("Markers from {0}", fn), file, gpxLayer); 150 if (ml.data.size() > 0) { 151 Main.main.addLayer(ml); 152 } 153 } 154 } 155 showNmeaInfobox(r.getNumberOfCoordinates()>0, r); 156 } else { 157 throw new IllegalStateException(); 158 } 159 } 160 161 private boolean asGpxData(String fn) { 162 return ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(fn); 163 } 164 165 private boolean asNmeaData(String fn) { 166 return ExtensionFileFilter.filters[ExtensionFileFilter.NMEA].acceptName(fn); 167 } 168 168 169 169 -
trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java
r1148 r1169 42 42 public class OpenLocationAction extends JosmAction { 43 43 44 45 46 47 48 49 50 44 /** 45 * Create an open action. The name is "Open a file". 46 */ 47 public OpenLocationAction() { 48 super(tr("Open Location..."), "openlocation", tr("Open a URL."), 49 Shortcut.registerShortcut("system:open_location", tr("File: {0}", tr("Open Location...")), KeyEvent.VK_L, Shortcut.GROUP_MENU), true); 50 } 51 51 52 52 public void actionPerformed(ActionEvent e) { 53 53 54 55 56 57 58 59 60 61 62 63 64 65 54 JCheckBox layer = new JCheckBox(tr("Separate Layer")); 55 layer.setSelected(Main.pref.getBoolean("download.newlayer")); 56 JPanel all = new JPanel(new GridBagLayout()); 57 all.add(new JLabel("Enter URL to download:"), GBC.eol()); 58 JTextField urltext = new JTextField(40); 59 all.add(urltext, GBC.eol()); 60 all.add(layer, GBC.eol()); 61 int answer = JOptionPane.showConfirmDialog(Main.parent, all, tr("Download Location"), JOptionPane.OK_CANCEL_OPTION); 62 if (answer != JOptionPane.OK_OPTION) 63 return; 64 openUrl(layer.isSelected(), urltext.getText()); 65 } 66 66 67 68 69 70 71 72 67 /** 68 * Open the given file. 69 */ 70 public void openUrl(boolean new_layer, String url) { 71 new DownloadOsmTask().loadUrl(new_layer, url); 72 } 73 73 74 74 } -
trunk/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
r1090 r1169 28 28 29 29 /** 30 * Align edges of a way so all angles are right angles. 31 * 30 * Align edges of a way so all angles are right angles. 31 * 32 32 * 1. Find orientation of all edges 33 33 * 2. Compute main orientation, weighted by length of edge, normalized to angles between 0 and pi/2 … … 38 38 public final class OrthogonalizeAction extends JosmAction { 39 39 40 41 super(tr("Orthogonalize shape"), 42 "ortho", 40 public OrthogonalizeAction() { 41 super(tr("Orthogonalize shape"), 42 "ortho", 43 43 tr("Move nodes so all angles are 90 or 270deg"), 44 Shortcut.registerShortcut("tools:orthogonalize", tr("Tool: {0}", tr("Orthogonalize")), 45 KeyEvent.VK_Q, 44 Shortcut.registerShortcut("tools:orthogonalize", tr("Tool: {0}", tr("Orthogonalize")), 45 KeyEvent.VK_Q, 46 46 Shortcut.GROUP_EDIT), true); 47 47 } 48 48 49 49 public void actionPerformed(ActionEvent e) { … … 60 60 JOptionPane.showMessageDialog(Main.parent, tr("Only two nodes allowed")); 61 61 return; 62 } 62 } 63 63 dirnodes.add((Node) osm); 64 64 continue; … … 68 68 JOptionPane.showMessageDialog(Main.parent, tr("Selection must consist only of ways.")); 69 69 return; 70 } 70 } 71 71 72 72 // Check if every way is made of at least four segments and closed … … 78 78 79 79 // Check if every edge in the way is a definite edge of at least 45 degrees of direction change 80 // Otherwise, two segments could be turned into same direction and intersection would fail. 80 // Otherwise, two segments could be turned into same direction and intersection would fail. 81 81 // Or changes of shape would be too serious. 82 82 for (int i1=0; i1 < way.nodes.size()-1; i1++) { … … 116 116 boolean use_dirnodes = false; 117 117 118 if (dirnodes.size() == 2) { 119 // When selection contains two nodes, use the nodes to compute a direction 118 if (dirnodes.size() == 2) { 119 // When selection contains two nodes, use the nodes to compute a direction 120 120 // to align all ways to 121 121 align_to_heading = normalize_angle(dirnodes.get(0).eastNorth.heading(dirnodes.get(1).eastNorth)); … … 124 124 125 125 for (OsmPrimitive osm : sel) { 126 if(!(osm instanceof Way)) 126 if(!(osm instanceof Way)) 127 127 continue; 128 128 … … 164 164 165 165 if (angle_diff_max > Math.PI/3) { 166 // rearrange headings: everything < 0 gets PI/2-rotated 166 // rearrange headings: everything < 0 gets PI/2-rotated 167 167 for (int i=0; i < sides; i++) { 168 if (headings[i] < 0) 168 if (headings[i] < 0) 169 169 headings[i] += Math.PI/2; 170 170 } … … 183 183 sum_weights += weights[i]; 184 184 } 185 align_to_heading = normalize_angle(sum_weighted_headings/sum_weights); 185 align_to_heading = normalize_angle(sum_weighted_headings/sum_weights); 186 186 } 187 187 188 188 189 189 for (int i=0; i < sides; i++) { 190 // Compute handy indices of three nodes to be used in one loop iteration. 191 // We use segments (i1,i2) and (i2,i3), align them and compute the new 190 // Compute handy indices of three nodes to be used in one loop iteration. 191 // We use segments (i1,i2) and (i2,i3), align them and compute the new 192 192 // position of the i2-node as the intersection of the realigned (i1,i2), (i2,i3) segments 193 193 // Not the most efficient algorithm, but we don't handle millions of nodes... … … 212 212 213 213 // compute intersection of segments 214 double u=det(B.east() - A.east(), B.north() - A.north(), 214 double u=det(B.east() - A.east(), B.north() - A.north(), 215 215 C.east() - D.east(), C.north() - D.north()); 216 216 217 217 // Check for parallel segments and do nothing if they are 218 // In practice this will probably only happen when a way has 218 // In practice this will probably only happen when a way has 219 219 // been duplicated 220 220 … … 225 225 // if the segment is scaled to length 1 226 226 227 double q = det(B.north() - C.north(), B.east() - C.east(), 227 double q = det(B.north() - C.north(), B.east() - C.east(), 228 228 D.north() - C.north(), D.east() - C.east()) / u; 229 229 EastNorth intersection = new EastNorth( … … 239 239 cmds.add(new MoveCommand(n, dx, dy)); 240 240 } 241 } 241 } 242 242 } 243 243 -
trunk/src/org/openstreetmap/josm/actions/PasteAction.java
r1084 r1169 29 29 30 30 public PasteAction() { 31 32 33 31 super(tr("Paste"), "paste", tr("Paste contents of paste buffer."), 32 Shortcut.registerShortcut("system:paste", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_V, Shortcut.GROUP_MENU), true); 33 setEnabled(false); 34 34 } 35 35 36 37 36 public void actionPerformed(ActionEvent e) { 37 DataSet pasteBuffer = Main.pasteBuffer; 38 38 39 40 41 42 43 44 45 46 47 48 39 /* Find the middle of the pasteBuffer area */ 40 double maxEast = -1E100, minEast = 1E100, maxNorth = -1E100, minNorth = 1E100; 41 for (Node n : pasteBuffer.nodes) { 42 double east = n.eastNorth.east(); 43 double north = n.eastNorth.north(); 44 if (east > maxEast) { maxEast = east; } 45 if (east < minEast) { minEast = east; } 46 if (north > maxNorth) { maxNorth = north; } 47 if (north < minNorth) { minNorth = north; } 48 } 49 49 50 51 52 53 54 55 50 EastNorth mPosition; 51 if((e.getModifiers() & ActionEvent.CTRL_MASK) ==0){ 52 mPosition = Main.map.mapView.getCenter(); 53 } else { 54 mPosition = Main.map.mapView.getEastNorth(Main.map.mapView.lastMEvent.getX(), Main.map.mapView.lastMEvent.getY()); 55 } 56 56 57 58 57 double offsetEast = mPosition.east() - (maxEast + minEast)/2.0; 58 double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0; 59 59 60 61 60 HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>(); 61 /* temporarily maps old nodes to new so we can do a true deep copy */ 62 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 63 /* do the deep copy of the paste buffer contents, leaving the pasteBuffer unchanged */ 64 for (Node n : pasteBuffer.nodes) { 65 Node nnew = new Node(n); 66 nnew.id = 0; 67 /* adjust the coordinates to the middle of the visible map area */ 68 nnew.eastNorth = new EastNorth(nnew.eastNorth.east() + offsetEast, nnew.eastNorth.north() + offsetNorth); 69 nnew.coor = Main.proj.eastNorth2latlon(nnew.eastNorth); 70 map.put(n, nnew); 71 } 72 for (Way w : pasteBuffer.ways) { 73 Way wnew = new Way(); 74 wnew.cloneFrom(w); 75 wnew.id = 0; 76 /* make sure we reference the new nodes corresponding to the old ones */ 77 List<Node> nodes = new ArrayList<Node>(); 78 for (Node n : w.nodes) { 79 nodes.add((Node)map.get(n)); 80 } 81 wnew.nodes.clear(); 82 wnew.nodes.addAll(nodes); 83 map.put(w, wnew); 84 } 85 for (Relation r : pasteBuffer.relations) { 86 Relation rnew = new Relation(r); 87 rnew.id = 0; 88 List<RelationMember> members = new ArrayList<RelationMember>(); 89 for (RelationMember m : r.members) { 90 RelationMember mnew = new RelationMember(m); 91 mnew.member = map.get(m.member); 92 members.add(mnew); 93 } 94 rnew.members.clear(); 95 rnew.members.addAll(members); 96 map.put(r, rnew); 97 } 98 98 99 100 101 102 103 104 99 /* Now execute the commands to add the dupicated contents of the paste buffer to the map */ 100 Collection<OsmPrimitive> osms = map.values(); 101 Collection<Command> clist = new LinkedList<Command>(); 102 for (OsmPrimitive osm : osms) { 103 clist.add(new AddCommand(osm)); 104 } 105 105 106 107 108 106 Main.main.undoRedo.add(new SequenceCommand(tr("Paste"), clist)); 107 Main.ds.setSelected(osms); 108 Main.map.mapView.repaint(); 109 109 } 110 110 } -
trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java
r1084 r1169 24 24 public final class PasteTagsAction extends JosmAction implements SelectionChangedListener { 25 25 26 27 28 29 30 31 32 33 26 public PasteTagsAction(JosmAction copyAction) { 27 super(tr("Paste Tags"), "pastetags", 28 tr("Apply tags of contents of paste buffer to all selected items."), 29 Shortcut.registerShortcut("system:pastestyle", tr("Edit: {0}", tr("Paste Tags")), KeyEvent.VK_V, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true); 30 DataSet.selListeners.add(this); 31 copyAction.addListener(this); 32 setEnabled(false); 33 } 34 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 35 private void pasteKeys(Collection<Command> clist, Collection<? extends OsmPrimitive> pasteBufferSubset, Collection<OsmPrimitive> selectionSubset) { 36 /* scan the paste buffer, and add tags to each of the selected objects. 37 * If a tag already exists, it is overwritten */ 38 if (selectionSubset != null && ! selectionSubset.isEmpty()) { 39 for (Iterator<? extends OsmPrimitive> it = pasteBufferSubset.iterator(); it.hasNext();) { 40 OsmPrimitive osm = it.next(); 41 Map<String, String> m = osm.keys; 42 if(m != null) 43 { 44 for (String key : m.keySet()) { 45 if (! key.equals("created_by")) 46 clist.add(new ChangePropertyCommand(selectionSubset, key, osm.keys.get(key))); 47 } 48 } 49 } 50 } 51 } 52 52 53 54 55 56 57 58 59 60 61 53 public void actionPerformed(ActionEvent e) { 54 Collection<Command> clist = new LinkedList<Command>(); 55 pasteKeys(clist, Main.pasteBuffer.nodes, Main.ds.getSelectedNodes()); 56 pasteKeys(clist, Main.pasteBuffer.ways, Main.ds.getSelectedWays()); 57 pasteKeys(clist, Main.pasteBuffer.relations, Main.ds.getSelectedRelations()); 58 Main.main.undoRedo.add(new SequenceCommand(tr("Paste Tags"), clist)); 59 Main.ds.setSelected(Main.ds.getSelected()); // to force selection listeners, in particular the tag panel, to update 60 Main.map.mapView.repaint(); 61 } 62 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 63 private boolean containsSameKeysWithDifferentValues(Collection<? extends OsmPrimitive> osms) { 64 Map<String,String> kvSeen = new HashMap<String,String>(); 65 for (Iterator<? extends OsmPrimitive> it = osms.iterator(); it.hasNext();) { 66 OsmPrimitive osm = it.next(); 67 if (osm.keys == null || osm.keys.isEmpty()) 68 continue; 69 for (String key : osm.keys.keySet()) { 70 if (key.equals("created_by")) // we ignore created_by 71 continue; 72 String value = osm.keys.get(key); 73 if (! kvSeen.containsKey(key)) 74 kvSeen.put(key, value); 75 else if (! kvSeen.get(key).equals(value)) 76 return true; 77 } 78 } 79 return false; 80 } 81 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 82 /** 83 * Determines whether to enable the widget depending on the contents of the paste 84 * buffer and current selection 85 * @param pasteBuffer 86 */ 87 private void possiblyEnable(Collection<? extends OsmPrimitive> selection, DataSet pasteBuffer) { 88 /* only enable if there is something selected to paste into and 89 if we don't have conflicting keys in the pastebuffer */ 90 setEnabled(selection != null && 91 ! selection.isEmpty() && 92 ! pasteBuffer.allPrimitives().isEmpty() && 93 (Main.ds.getSelectedNodes().isEmpty() || 94 ! containsSameKeysWithDifferentValues(pasteBuffer.nodes)) && 95 (Main.ds.getSelectedWays().isEmpty() || 96 ! containsSameKeysWithDifferentValues(pasteBuffer.ways)) && 97 (Main.ds.getSelectedRelations().isEmpty() || 98 ! containsSameKeysWithDifferentValues(pasteBuffer.relations))); 99 } 100 100 101 102 103 101 @Override public void pasteBufferChanged(DataSet newPasteBuffer) { 102 possiblyEnable(Main.ds.getSelected(), newPasteBuffer); 103 } 104 104 105 106 107 105 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 106 possiblyEnable(newSelection, Main.pasteBuffer); 107 } 108 108 } -
trunk/src/org/openstreetmap/josm/actions/PreferencesAction.java
r1084 r1169 25 25 public class PreferencesAction extends JosmAction { 26 26 27 28 29 30 31 32 33 27 /** 28 * Create the preference action with "&Preferences" as label. 29 */ 30 public PreferencesAction() { 31 super(tr("Preferences ..."), "preference", tr("Open a preferences page for global settings."), 32 Shortcut.registerShortcut("system:preferences", tr("Preferences"), KeyEvent.VK_F12, Shortcut.GROUP_DIRECT), true); 33 } 34 34 35 36 37 38 39 40 41 42 35 /** 36 * Launch the preferences dialog. 37 */ 38 public void actionPerformed(ActionEvent e) { 39 PreferenceDialog prefDlg = new PreferenceDialog(); 40 prefDlg.setMinimumSize(new Dimension(400,300)); 41 JPanel prefPanel = new JPanel(new GridBagLayout()); 42 prefPanel.add(prefDlg, GBC.eol().fill(GBC.BOTH)); 43 43 44 45 46 47 44 JOptionPane pane = new JOptionPane(prefPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); 45 JDialog dlg = pane.createDialog(Main.parent, tr("Preferences")); 46 dlg.setResizable(true); 47 dlg.setMinimumSize(new Dimension(500,400)); 48 48 49 // 50 // 51 // 52 // 49 // if (dlg.getWidth() > 600) 50 // dlg.setSize(600, dlg.getHeight()); 51 // if (dlg.getHeight() > 600) 52 // dlg.setSize(dlg.getWidth(),600); 53 53 54 55 54 int JOSMWidth = Main.parent.getWidth(); 55 int JOSMHeight = Main.parent.getHeight(); 56 56 57 58 59 57 if (JOSMWidth > 2000 && JOSMWidth > JOSMHeight * 2) 58 // don't center on horizontal span monitor configurations (yes, can be selfish sometimes) 59 JOSMWidth /= 2; 60 60 61 62 63 64 65 66 61 int targetWidth = JOSMWidth / 2; 62 if (targetWidth < 600) targetWidth = 600; 63 if (targetWidth > 1200) targetWidth = 1200; 64 int targetHeight = (JOSMHeight * 3) / 4; 65 if (targetHeight < 600) targetHeight = 600; 66 if (targetHeight > 1200) targetHeight = 1200; 67 67 68 69 68 int targetX = Main.parent.getX() + JOSMWidth / 2 - targetWidth / 2; 69 int targetY = Main.parent.getY() + JOSMHeight / 2 - targetHeight / 2; 70 70 71 71 dlg.setBounds(targetX, targetY, targetWidth, targetHeight); 72 72 73 74 75 76 73 dlg.setVisible(true); 74 if (pane.getValue() instanceof Integer && (Integer)pane.getValue() == JOptionPane.OK_OPTION) 75 prefDlg.ok(); 76 } 77 77 } -
trunk/src/org/openstreetmap/josm/actions/RedoAction.java
r1084 r1169 17 17 public class RedoAction extends JosmAction { 18 18 19 20 21 22 23 24 25 26 19 /** 20 * Construct the action with "Redo" as label. 21 */ 22 public RedoAction() { 23 super(tr("Redo"), "redo", tr("Redo the last undone action."), 24 Shortcut.registerShortcut("system:redo", tr("Edit: {0}", tr("Redo")), KeyEvent.VK_Y, Shortcut.GROUP_MENU), true); 25 setEnabled(false); 26 } 27 27 28 29 30 31 32 33 28 public void actionPerformed(ActionEvent e) { 29 if (Main.map == null) 30 return; 31 Main.map.repaint(); 32 Main.main.undoRedo.redo(); 33 } 34 34 } -
trunk/src/org/openstreetmap/josm/actions/RenameLayerAction.java
r655 r1169 21 21 * Action to rename an specific layer. Provides the option to rename the 22 22 * file, this layer was loaded from as well (if it was loaded from a file). 23 * 23 * 24 24 * @author Imi 25 25 */ 26 26 public class RenameLayerAction extends AbstractAction { 27 27 28 29 28 private File file; 29 private Layer layer; 30 30 31 32 33 * If null, no possibility to "rename the file as well" is provided. 34 35 36 37 38 39 40 31 /** 32 * @param file The file of the original location of this layer. 33 * If null, no possibility to "rename the file as well" is provided. 34 */ 35 public RenameLayerAction(File file, Layer layer) { 36 super(tr("Rename layer"), ImageProvider.get("dialogs", "edit")); 37 this.file = file; 38 this.layer = layer; 39 this.putValue("help", "Action/LayerRename"); 40 } 41 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 42 public void actionPerformed(ActionEvent e) { 43 Box panel = Box.createVerticalBox(); 44 final JTextField name = new JTextField(layer.name); 45 panel.add(name); 46 JCheckBox filerename = new JCheckBox(tr("Also rename the file")); 47 if (Main.applet) { 48 filerename.setEnabled(false); 49 filerename.setSelected(false); 50 } else { 51 panel.add(filerename); 52 filerename.setEnabled(file != null); 53 } 54 if (filerename.isEnabled()) 55 filerename.setSelected(Main.pref.getBoolean("layer.rename-file", true)); 56 56 57 58 59 60 61 62 63 64 57 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){ 58 @Override public void selectInitialValue() { 59 name.requestFocusInWindow(); 60 name.selectAll(); 61 } 62 }; 63 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Rename layer")); 64 dlg.setVisible(true); 65 65 66 67 68 69 70 66 Object answer = optionPane.getValue(); 67 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE || 68 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) { 69 return; 70 } 71 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 72 String nameText = name.getText(); 73 if (filerename.isEnabled()) { 74 Main.pref.put("layer.rename-file", filerename.isSelected()); 75 if (filerename.isSelected()) { 76 String newname = nameText; 77 if (newname.indexOf("/") == -1 && newname.indexOf("\\") == -1) 78 newname = file.getParent() + File.separator + newname; 79 String oldname = file.getName(); 80 if (name.getText().indexOf('.') == -1 && oldname.indexOf('.') >= 0) 81 newname += oldname.substring(oldname.lastIndexOf('.')); 82 File newFile = new File(newname); 83 if (file.renameTo(newFile)) { 84 layer.associatedFile = newFile; 85 nameText = newFile.getName(); 86 } else { 87 JOptionPane.showMessageDialog(Main.parent, tr("Could not rename the file \"{0}\".", file.getPath())); 88 return; 89 } 90 } 91 } 92 layer.name = nameText; 93 Main.parent.repaint(); 94 } 95 95 } -
trunk/src/org/openstreetmap/josm/actions/ReverseWayAction.java
r1084 r1169 28 28 public final class ReverseWayAction extends JosmAction { 29 29 30 31 32 33 30 public ReverseWayAction() { 31 super(tr("Reverse ways"), "wayflip", tr("Reverse the direction of all selected ways."), 32 Shortcut.registerShortcut("tools:reverse", tr("Tool: {0}", tr("Reverse ways")), KeyEvent.VK_R, Shortcut.GROUP_EDIT), true); 33 } 34 34 35 36 37 38 39 35 public void actionPerformed(ActionEvent e) { 36 final Collection<Way> sel = new LinkedList<Way>(); 37 new Visitor() { 38 public void visit(Node n) { 39 } 40 40 41 42 43 41 public void visit(Way w) { 42 sel.add(w); 43 } 44 44 45 46 45 public void visit(Relation e) { 46 } 47 47 48 49 50 51 52 48 public void visitAll() { 49 for (OsmPrimitive osm : Main.ds.getSelected()) 50 osm.visit(this); 51 } 52 }.visitAll(); 53 53 54 55 56 57 58 54 if (sel.isEmpty()) { 55 JOptionPane.showMessageDialog(Main.parent, 56 tr("Please select at least one way.")); 57 return; 58 } 59 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 60 boolean propertiesUpdated = false; 61 ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector(); 62 Collection<Command> c = new LinkedList<Command>(); 63 for (Way w : sel) { 64 Way wnew = new Way(w); 65 Collections.reverse(wnew.nodes); 66 if (Main.pref.getBoolean("tag-correction.reverse-way", true)) { 67 try 68 { 69 final Collection<Command> changePropertyCommands = reverseWayTagCorrector.execute(wnew); 70 propertiesUpdated = propertiesUpdated 71 || (changePropertyCommands != null && !changePropertyCommands.isEmpty()); 72 c.addAll(changePropertyCommands); 73 } 74 catch(UserCancelException ex) 75 { 76 return; 77 } 78 } 79 c.add(new ChangeCommand(w, wnew)); 80 } 81 Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c)); 82 if (propertiesUpdated) 83 DataSet.fireSelectionChanged(Main.ds.getSelected()); 84 Main.map.repaint(); 85 } 86 86 } -
trunk/src/org/openstreetmap/josm/actions/SaveAction.java
r1084 r1169 19 19 public class SaveAction extends SaveActionBase { 20 20 21 22 23 24 25 26 27 28 21 /** 22 * Construct the action with "Save" as label. 23 * @param layer Save this layer. 24 */ 25 public SaveAction(Layer layer) { 26 super(tr("Save"), "save", tr("Save the current data."), 27 Shortcut.registerShortcut("system:save", tr("File: {0}", tr("Save")), KeyEvent.VK_S, Shortcut.GROUP_MENU), layer); 28 } 29 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 30 @Override public File getFile(Layer layer) { 31 if (layer instanceof OsmDataLayer) { 32 File f = ((OsmDataLayer)layer).associatedFile; 33 if (f != null) { 34 return f; 35 } 36 } 37 if (layer instanceof GpxLayer) { 38 File f = ((GpxLayer)layer).data.storageFile; 39 if (f != null) { 40 return f; 41 } 42 } 43 return openFileDialog(layer); 44 } 45 45 } -
trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
r1084 r1169 26 26 public abstract class SaveActionBase extends DiskAccessAction { 27 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 28 private Layer layer; 29 30 public SaveActionBase(String name, String iconName, String tooltip, Shortcut shortcut, Layer layer) { 31 super(name, iconName, tooltip, shortcut); 32 this.layer = layer; 33 } 34 35 @Deprecated 36 public SaveActionBase(String name, String iconName, String tooltip, int shortcut, int modifiers, Layer layer) { 37 super(name, iconName, tooltip, shortcut, modifiers); 38 this.layer = layer; 39 } 40 41 public void actionPerformed(ActionEvent e) { 42 Layer layer = this.layer; 43 if (layer == null && Main.map != null && (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer 44 || Main.map.mapView.getActiveLayer() instanceof GpxLayer)) 45 layer = Main.map.mapView.getActiveLayer(); 46 if (layer == null) 47 layer = Main.main.editLayer(); 48 49 if (!checkSaveConditions(layer)) 50 return; 51 52 53 File file = getFile(layer); 54 if (file == null) 55 return; 56 57 save(file, layer); 58 59 layer.name = file.getName(); 60 layer.associatedFile = file; 61 Main.parent.repaint(); 62 } 63 64 protected abstract File getFile(Layer layer); 65 66 /** 67 * Checks whether it is ok to launch a save (whether we have data, 68 * there is no conflict etc.) 69 * @return <code>true</code>, if it is safe to save. 70 */ 71 public boolean checkSaveConditions(Layer layer) { 72 if (layer == null) { 73 JOptionPane.showMessageDialog(Main.parent, tr("Internal Error: cannot check conditions for no layer. Please report this as a bug.")); 74 return false; 75 } 76 if (Main.map == null) { 77 JOptionPane.showMessageDialog(Main.parent, tr("No document open so nothing to save.")); 78 return false; 79 } 80 81 if (layer instanceof OsmDataLayer && isDataSetEmpty((OsmDataLayer)layer) && JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(Main.parent,tr("The document contains no data. Save anyway?"), tr("Empty document"), JOptionPane.YES_NO_OPTION)) { 82 return false; 83 } 84 if (layer instanceof GpxLayer && ((GpxLayer)layer).data == null) { 85 return false; 86 } 87 if (!Main.map.conflictDialog.conflicts.isEmpty()) { 88 int answer = JOptionPane.showConfirmDialog(Main.parent, 89 tr("There are unresolved conflicts. Conflicts will not be saved and handled as if you rejected all. Continue?"),tr("Conflicts"), JOptionPane.YES_NO_OPTION); 90 if (answer != JOptionPane.YES_OPTION) 91 return false; 92 } 93 return true; 94 } 95 96 public static File openFileDialog(Layer layer) { 97 JFileChooser fc = createAndOpenFileChooser(false, false, layer instanceof GpxLayer ? tr("Save GPX file") : tr("Save OSM file")); 98 if (fc == null) 99 return null; 100 101 File file = fc.getSelectedFile(); 102 103 String fn = file.getPath(); 104 if (fn.indexOf('.') == -1) { 105 FileFilter ff = fc.getFileFilter(); 106 if (ff instanceof ExtensionFileFilter) 107 fn += "." + ((ExtensionFileFilter)ff).defaultExtension; 108 else if (layer instanceof GpxLayer) 109 fn += ".gpx"; 110 else 111 fn += ".osm"; 112 file = new File(fn); 113 } 114 return file; 115 } 116 117 private static void copy(File src, File dst) throws IOException { 118 FileInputStream srcStream; 119 FileOutputStream dstStream; 120 try { 121 srcStream = new FileInputStream(src); 122 dstStream = new FileOutputStream(dst); 123 } catch (FileNotFoundException e) { 124 JOptionPane.showMessageDialog(Main.parent, tr("Could not back up file.")+"\n"+e.getMessage()); 125 return; 126 } 127 byte buf[] = new byte[1<<16]; 128 int len; 129 while ((len = srcStream.read(buf)) != -1) { 130 dstStream.write(buf, 0, len); 131 } 132 srcStream.close(); 133 dstStream.close(); 134 } 135 136 public static void save(File file, Layer layer) { 137 if (layer instanceof GpxLayer) { 138 save(file, (GpxLayer)layer); 139 ((GpxLayer)layer).data.storageFile = file; 140 } else if (layer instanceof OsmDataLayer) { 141 save(file, (OsmDataLayer)layer); 142 } 143 } 144 145 public static void save(File file, OsmDataLayer layer) { 146 File tmpFile = null; 147 try { 148 if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) { 149 GpxExportAction.exportGpx(file, layer); 150 } else if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(file.getPath())) { 151 // use a tmp file because if something errors out in the 152 // process of writing the file, we might just end up with 153 // a truncated file. That can destroy lots of work. 154 if (file.exists()) { 155 tmpFile = new File(file.getPath() + "~"); 156 copy(file, tmpFile); 157 } 158 OsmWriter.output(new FileOutputStream(file), new OsmWriter.All(layer.data, false)); 159 if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null)) 160 tmpFile.delete(); 161 } else { 162 JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension.")); 163 return; 164 } 165 layer.cleanData(null, false); 166 } catch (IOException e) { 167 e.printStackTrace(); 168 JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage()); 169 170 try { 171 // if the file save failed, then the tempfile will not 172 // be deleted. So, restore the backup if we made one. 173 if (tmpFile != null && tmpFile.exists()) { 174 copy(tmpFile, file); 175 } 176 } catch (IOException e2) { 177 e2.printStackTrace(); 178 JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e2.getMessage()); 179 } 180 } 181 } 182 183 public static void save(File file, GpxLayer layer) { 184 File tmpFile = null; 185 try { 186 if (ExtensionFileFilter.filters[ExtensionFileFilter.GPX].acceptName(file.getPath())) { 187 188 // use a tmp file because if something errors out in the 189 // process of writing the file, we might just end up with 190 // a truncated file. That can destroy lots of work. 191 if (file.exists()) { 192 tmpFile = new File(file.getPath() + "~"); 193 copy(file, tmpFile); 194 } 195 FileOutputStream fo = new FileOutputStream(file); 196 new GpxWriter(fo).write(layer.data); 197 fo.flush(); 198 fo.close(); 199 200 if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null)) { 201 tmpFile.delete(); 202 } 203 } else { 204 JOptionPane.showMessageDialog(Main.parent, tr("Unknown file extension.")); 205 return; 206 } 207 } catch (IOException e) { 208 e.printStackTrace(); 209 JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while saving.")+"\n"+e.getMessage()); 210 } 211 try { 212 // if the file save failed, then the tempfile will not 213 // be deleted. So, restore the backup if we made one. 214 if (tmpFile != null && tmpFile.exists()) { 215 copy(tmpFile, file); 216 } 217 } catch (IOException e) { 218 e.printStackTrace(); 219 JOptionPane.showMessageDialog(Main.parent, tr("An error occurred while restoring backup file.")+"\n"+e.getMessage()); 220 } 221 } 222 223 /** 224 * Check the data set if it would be empty on save. It is empty, if it contains 225 * no objects (after all objects that are created and deleted without being 226 * transfered to the server have been removed). 227 * 228 * @return <code>true</code>, if a save result in an empty data set. 229 */ 230 private boolean isDataSetEmpty(OsmDataLayer layer) { 231 for (OsmPrimitive osm : layer.data.allNonDeletedPrimitives()) 232 if (!osm.deleted || osm.id > 0) 233 return false; 234 return true; 235 } 236 236 } -
trunk/src/org/openstreetmap/josm/actions/SaveAsAction.java
r1084 r1169 17 17 public class SaveAsAction extends SaveActionBase { 18 18 19 20 21 22 23 24 25 26 19 /** 20 * Construct the action with "Save" as label. 21 * @param layer Save this layer. 22 */ 23 public SaveAsAction(Layer layer) { 24 super(tr("Save as ..."), "save_as", tr("Save the current data to a new file."), 25 Shortcut.registerShortcut("system:saveas", tr("File: {0}", tr("Save as ...")), KeyEvent.VK_S, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), layer); 26 } 27 27 28 29 30 28 @Override protected File getFile(Layer layer) { 29 return openFileDialog(layer); 30 } 31 31 } -
trunk/src/org/openstreetmap/josm/actions/SelectAllAction.java
r1084 r1169 12 12 public class SelectAllAction extends JosmAction { 13 13 14 15 16 17 14 public SelectAllAction() { 15 super(tr("Select All"),"selectall", tr("Select all undeleted objects in the data layer. This selects incomplete objects too."), 16 Shortcut.registerShortcut("system:selectall", tr("Edit: {0}", tr("Select All")), KeyEvent.VK_A, Shortcut.GROUP_MENU), true); 17 } 18 18 19 20 21 19 public void actionPerformed(ActionEvent e) { 20 Main.ds.setSelected(Main.ds.allNonDeletedPhysicalPrimitives()); 21 } 22 22 } -
trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java
r1084 r1169 44 44 public class SplitWayAction extends JosmAction implements SelectionChangedListener { 45 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 } 82 83 84 } 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 46 private Way selectedWay; 47 private List<Node> selectedNodes; 48 49 /** 50 * Create a new SplitWayAction. 51 */ 52 public SplitWayAction() { 53 super(tr("Split Way"), "splitway", tr("Split a way at the selected node."), 54 Shortcut.registerShortcut("tools:splitway", tr("Tool: {0}", tr("Split Way")), KeyEvent.VK_P, Shortcut.GROUP_EDIT), true); 55 DataSet.selListeners.add(this); 56 } 57 58 /** 59 * Called when the action is executed. 60 * 61 * This method performs an expensive check whether the selection clearly defines one 62 * of the split actions outlined above, and if yes, calls the splitWay method. 63 */ 64 public void actionPerformed(ActionEvent e) { 65 66 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 67 68 if (!checkSelection(selection)) { 69 JOptionPane.showMessageDialog(Main.parent, tr("The current selection cannot be used for splitting.")); 70 return; 71 } 72 73 selectedWay = null; 74 selectedNodes = null; 75 76 Visitor splitVisitor = new Visitor(){ 77 public void visit(Node n) { 78 if (selectedNodes == null) 79 selectedNodes = new LinkedList<Node>(); 80 selectedNodes.add(n); 81 } 82 public void visit(Way w) { 83 selectedWay = w; 84 } 85 public void visit(Relation e) { 86 // enties are not considered 87 } 88 }; 89 90 for (OsmPrimitive p : selection) 91 p.visit(splitVisitor); 92 93 // If only nodes are selected, try to guess which way to split. This works if there 94 // is exactly one way that all nodes are part of. 95 if (selectedWay == null && selectedNodes != null) { 96 HashMap<Way, Integer> wayOccurenceCounter = new HashMap<Way, Integer>(); 97 for (Node n : selectedNodes) { 98 for (Way w : Main.ds.ways) { 99 if (w.deleted || w.incomplete) continue; 100 int last = w.nodes.size()-1; 101 if(last <= 0) continue; // zero or one node ways 102 Boolean circular = w.nodes.get(0).equals(w.nodes.get(last)); 103 int i = 0; 104 for (Node wn : w.nodes) { 105 if ((circular || (i > 0 && i < last)) && n.equals(wn)) { 106 Integer old = wayOccurenceCounter.get(w); 107 wayOccurenceCounter.put(w, (old == null) ? 1 : old+1); 108 break; 109 } 110 i++; 111 } 112 } 113 } 114 if (wayOccurenceCounter.isEmpty()) { 115 JOptionPane.showMessageDialog(Main.parent, 116 trn("The selected node is no inner part of any way.", 117 "The selected nodes are no inner part of any way.", selectedNodes.size())); 118 return; 119 } 120 121 for (Entry<Way, Integer> entry : wayOccurenceCounter.entrySet()) { 122 if (entry.getValue().equals(selectedNodes.size())) { 123 if (selectedWay != null) { 124 JOptionPane.showMessageDialog(Main.parent, tr("There is more than one way using the node(s) you selected. Please select the way also.")); 125 return; 126 } 127 selectedWay = entry.getKey(); 128 } 129 } 130 131 if (selectedWay == null) { 132 JOptionPane.showMessageDialog(Main.parent, tr("The selected nodes do not share the same way.")); 133 return; 134 } 135 136 // If a way and nodes are selected, verify that the nodes are part of the way. 137 } else if (selectedWay != null && selectedNodes != null) { 138 139 HashSet<Node> nds = new HashSet<Node>(selectedNodes); 140 for (Node n : selectedWay.nodes) { 141 nds.remove(n); 142 } 143 if (!nds.isEmpty()) { 144 JOptionPane.showMessageDialog(Main.parent, 145 trn("The selected way does not contain the selected node.", 146 "The selected way does not contain all the selected nodes.", selectedNodes.size())); 147 return; 148 } 149 } 150 151 // and then do the work. 152 splitWay(); 153 } 154 155 /** 156 * Checks if the selection consists of something we can work with. 157 * Checks only if the number and type of items selected looks good; 158 * does not check whether the selected items are really a valid 159 * input for splitting (this would be too expensive to be carried 160 * out from the selectionChanged listener). 161 */ 162 private boolean checkSelection(Collection<? extends OsmPrimitive> selection) { 163 boolean way = false; 164 boolean node = false; 165 for (OsmPrimitive p : selection) { 166 if (p instanceof Way && !way) { 167 way = true; 168 } else if (p instanceof Node) { 169 node = true; 170 } else { 171 return false; 172 } 173 } 174 return node; 175 } 176 177 /** 178 * Split a way into two or more parts, starting at a selected node. 179 */ 180 private void splitWay() { 181 // We take our way's list of nodes and copy them to a way chunk (a 182 // list of nodes). Whenever we stumble upon a selected node, we start 183 // a new way chunk. 184 185 Set<Node> nodeSet = new HashSet<Node>(selectedNodes); 186 List<List<Node>> wayChunks = new LinkedList<List<Node>>(); 187 List<Node> currentWayChunk = new ArrayList<Node>(); 188 wayChunks.add(currentWayChunk); 189 190 Iterator<Node> it = selectedWay.nodes.iterator(); 191 while (it.hasNext()) { 192 Node currentNode = it.next(); 193 boolean atEndOfWay = currentWayChunk.isEmpty() || !it.hasNext(); 194 currentWayChunk.add(currentNode); 195 if (nodeSet.contains(currentNode) && !atEndOfWay) { 196 currentWayChunk = new ArrayList<Node>(); 197 currentWayChunk.add(currentNode); 198 wayChunks.add(currentWayChunk); 199 } 200 } 201 202 // Handle circular ways specially. 203 // If you split at a circular way at two nodes, you just want to split 204 // it at these points, not also at the former endpoint. 205 // So if the last node is the same first node, join the last and the 206 // first way chunk. 207 List<Node> lastWayChunk = wayChunks.get(wayChunks.size() - 1); 208 if (wayChunks.size() >= 2 209 && wayChunks.get(0).get(0) == lastWayChunk.get(lastWayChunk.size() - 1) 210 && !nodeSet.contains(wayChunks.get(0).get(0))) { 211 if (wayChunks.size() == 2) { 212 JOptionPane.showMessageDialog(Main.parent, tr("You must select two or more nodes to split a circular way.")); 213 return; 214 } 215 lastWayChunk.remove(lastWayChunk.size() - 1); 216 lastWayChunk.addAll(wayChunks.get(0)); 217 wayChunks.remove(wayChunks.size() - 1); 218 wayChunks.set(0, lastWayChunk); 219 } 220 221 if (wayChunks.size() < 2) { 222 if(wayChunks.get(0).get(0) == wayChunks.get(0).get(wayChunks.get(0).size()-1)) 223 JOptionPane.showMessageDialog(Main.parent, tr("You must select two or more nodes to split a circular way.")); 224 else 225 JOptionPane.showMessageDialog(Main.parent, tr("The way cannot be split at the selected nodes. (Hint: Select nodes in the middle of the way.)")); 226 return; 227 } 228 //Main.debug("wayChunks.size(): " + wayChunks.size()); 229 //Main.debug("way id: " + selectedWay.id); 230 231 // build a list of commands, and also a new selection list 232 Collection<Command> commandList = new ArrayList<Command>(wayChunks.size()); 233 Collection<Way> newSelection = new ArrayList<Way>(wayChunks.size()); 234 235 Iterator<List<Node>> chunkIt = wayChunks.iterator(); 236 237 // First, change the original way 238 Way changedWay = new Way(selectedWay); 239 changedWay.nodes.clear(); 240 changedWay.nodes.addAll(chunkIt.next()); 241 commandList.add(new ChangeCommand(selectedWay, changedWay)); 242 newSelection.add(selectedWay); 243 244 Collection<Way> newWays = new ArrayList<Way>(); 245 // Second, create new ways 246 while (chunkIt.hasNext()) { 247 Way wayToAdd = new Way(); 248 if (selectedWay.keys != null) { 249 wayToAdd.keys = new HashMap<String, String>(selectedWay.keys); 250 wayToAdd.checkTagged(); 251 251 wayToAdd.checkDirectionTagged(); 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 252 } 253 newWays.add(wayToAdd); 254 wayToAdd.nodes.addAll(chunkIt.next()); 255 commandList.add(new AddCommand(wayToAdd)); 256 //Main.debug("wayToAdd: " + wayToAdd); 257 newSelection.add(wayToAdd); 258 259 } 260 Boolean warnme=false; 261 // now copy all relations to new way also 262 for (Relation r : Main.ds.relations) { 263 if (r.deleted || r.incomplete) continue; 264 for (RelationMember rm : r.members) { 265 if (rm.member instanceof Way) { 266 if (rm.member == selectedWay) 267 { 268 Relation c = new Relation(r); 269 for(Way wayToAdd : newWays) 270 { 271 RelationMember em = new RelationMember(); 272 em.member = wayToAdd; 273 em.role = rm.role; 274 if(em.role.length() > 0) 275 warnme = true; 276 c.members.add(em); 277 } 278 commandList.add(new ChangeCommand(r, c)); 279 break; 280 } 281 } 282 } 283 } 284 if(warnme) 285 JOptionPane.showMessageDialog(Main.parent, tr("A role based relation membership was copied to all new ways.\nYou should verify this and correct it when necessary.")); 286 287 NameVisitor v = new NameVisitor(); 288 v.visit(selectedWay); 289 Main.main.undoRedo.add( 290 new SequenceCommand(tr("Split way {0} into {1} parts", 291 v.name, wayChunks.size()), 292 commandList)); 293 Main.ds.setSelected(newSelection); 294 } 295 296 /** 297 * Enable the "split way" menu option if the selection looks like we could use it. 298 */ 299 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 300 setEnabled(checkSelection(newSelection)); 301 } 302 302 } -
trunk/src/org/openstreetmap/josm/actions/ToggleGPXLinesAction.java
r1138 r1169 12 12 public class ToggleGPXLinesAction extends JosmAction { 13 13 14 15 16 17 14 public ToggleGPXLinesAction() { 15 super(tr("Toggle GPX Lines"), "gps-lines", tr("Toggles the global setting ''{0}''.", tr("Draw lines between raw gps points.")), 16 Shortcut.registerShortcut("view:gpxlines", tr("View: {0}", tr("Toggle GPX Lines")), KeyEvent.VK_X, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true); 17 } 18 18 19 20 21 22 19 public void actionPerformed(ActionEvent e) { 20 Main.pref.put("draw.rawgps.lines", !Main.pref.getBoolean("draw.rawgps.lines")); 21 Main.map.mapView.repaint(); 22 } 23 23 } -
trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java
r1084 r1169 36 36 public class UnGlueAction extends JosmAction { //implements SelectionChangedListener { 37 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 38 private Node selectedNode; 39 private Way selectedWay; 40 private ArrayList<Node> selectedNodes; 41 42 /** 43 * Create a new UnGlueAction. 44 */ 45 public UnGlueAction() { 46 super(tr("UnGlue Ways"), "unglueways", tr("Duplicate nodes that are used by multiple ways."), 47 Shortcut.registerShortcut("tools:unglue", tr("Tool: {0}", tr("UnGlue Ways")), KeyEvent.VK_G, Shortcut.GROUP_EDIT), true); 48 //DataSet.selListeners.add(this); 49 } 50 51 /** 52 * Called when the action is executed. 53 * 54 * This method does some checking on the selection and calls the matching unGlueWay method. 55 */ 56 public void actionPerformed(ActionEvent e) { 57 58 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 59 60 if (checkSelection(selection)) { 61 int count = 0; 62 for (Way w : Main.ds.ways) { 63 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue; 64 if (!w.nodes.contains(selectedNode)) continue; 65 count++; 66 } 67 if (count < 2) { 68 JOptionPane.showMessageDialog(Main.parent, tr("This node is not glued to anything else.")); 69 } else { 70 // and then do the work. 71 unglueWays(); 72 } 73 } else if (checkSelection2(selection)) { 74 ArrayList<Node> tmpNodes = new ArrayList<Node>(); 75 for (Node n : selectedNodes) { 76 int count = 0; 77 for (Way w : Main.ds.ways) { 78 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue; 79 if (!w.nodes.contains(n)) continue; 80 count++; 81 } 82 if (count >= 2) { 83 tmpNodes.add(n); 84 } 85 } 86 if (tmpNodes.size() < 1) { 87 if (selection.size() > 1) { 88 JOptionPane.showMessageDialog(Main.parent, tr("None of these nodes is glued to anything else.")); 89 } else { 90 JOptionPane.showMessageDialog(Main.parent, tr("None of this way's nodes is glued to anything else.")); 91 } 92 } else { 93 // and then do the work. 94 selectedNodes = tmpNodes; 95 unglueWays2(); 96 } 97 } else { 98 JOptionPane.showMessageDialog(Main.parent, 99 tr("The current selection cannot be used for unglueing.")+"\n"+ 100 "\n"+ 101 tr("Select either:")+"\n"+ 102 tr("* One node that is used by more than one way, or")+"\n"+ 103 tr("* One node that is used by more than one way and one of those ways, or")+"\n"+ 104 tr("* One way that has one or more nodes that are used by more than one way, or")+"\n"+ 105 tr("* One way and one or more of its nodes that are used by more than one way.")+"\n"+ 106 "\n"+ 107 tr("Note: If a way is selected, this way will get fresh copies of the unglued\n"+ 108 "nodes and the new nodes will be selected. Otherwise, all ways will get their\n"+ 109 "own copy and all nodes will be selected.") 110 ); 111 } 112 selectedNode = null; 113 selectedWay = null; 114 selectedNodes = null; 115 } 116 117 /** 118 * Checks if the selection consists of something we can work with. 119 * Checks only if the number and type of items selected looks good; 120 * does not check whether the selected items are really a valid 121 * input for splitting (this would be too expensive to be carried 122 * out from the selectionChanged listener). 123 * 124 * If this method returns "true", selectedNode and selectedWay will 125 * be set. 126 * 127 * Returns true if either one node is selected or one node and one 128 * way are selected and the node is part of the way. 129 * 130 * The way will be put into the object variable "selectedWay", the 131 * node into "selectedNode". 132 */ 133 private boolean checkSelection(Collection<? extends OsmPrimitive> selection) { 134 135 int size = selection.size(); 136 if (size < 1 || size > 2) 137 return false; 138 139 selectedNode = null; 140 selectedWay = null; 141 142 for (OsmPrimitive p : selection) { 143 if (p instanceof Node) { 144 selectedNode = (Node) p; 145 if (size == 1 || selectedWay != null) 146 return size == 1 || selectedWay.nodes.contains(selectedNode); 147 } else if (p instanceof Way) { 148 selectedWay = (Way) p; 149 if (size == 2 && selectedNode != null) 150 return selectedWay.nodes.contains(selectedNode); 151 } 152 } 153 154 return false; 155 } 156 157 /** 158 * Checks if the selection consists of something we can work with. 159 * Checks only if the number and type of items selected looks good; 160 * does not check whether the selected items are really a valid 161 * input for splitting (this would be too expensive to be carried 162 * out from the selectionChanged listener). 163 * 164 * Returns true if one way and any number of nodes that are part of 165 * that way are selected. Note: "any" can be none, then all nodes of 166 * the way are used. 167 * 168 * The way will be put into the object variable "selectedWay", the 169 * nodes into "selectedNodes". 170 */ 171 private boolean checkSelection2(Collection<? extends OsmPrimitive> selection) { 172 if (selection.size() < 1) 173 return false; 174 175 selectedWay = null; 176 for (OsmPrimitive p : selection) { 177 if (p instanceof Way) { 178 if (selectedWay != null) { 179 return false; 180 } 181 selectedWay = (Way) p; 182 } 183 } 184 if (selectedWay == null) { 185 return false; 186 } 187 188 selectedNodes = new ArrayList<Node>(); 189 for (OsmPrimitive p : selection) { 190 if (p instanceof Node) { 191 Node n = (Node) p; 192 if (!selectedWay.nodes.contains(n)) { 193 return false; 194 } 195 selectedNodes.add(n); 196 } 197 } 198 199 if (selectedNodes.size() < 1) { 200 selectedNodes.addAll(selectedWay.nodes); 201 } 202 203 return true; 204 } 205 206 /** 207 * dupe the given node of the given way 208 * 209 * -> the new node will be put into the parameter newNodes. 210 * -> the add-node command will be put into the parameter cmds. 211 * -> the changed way will be returned and must be put into cmds by the caller! 212 */ 213 private Way modifyWay(Node originalNode, Way w, List<Command> cmds, List<Node> newNodes) { 214 ArrayList<Node> nn = new ArrayList<Node>(); 215 for (Node pushNode : w.nodes) { 216 if (originalNode == pushNode) { 217 // clone the node for all other ways 218 pushNode = new Node(pushNode); 219 pushNode.id = 0; 220 newNodes.add(pushNode); 221 cmds.add(new AddCommand(pushNode)); 222 } 223 nn.add(pushNode); 224 } 225 Way newWay = new Way(w); 226 newWay.nodes.clear(); 227 newWay.nodes.addAll(nn); 228 229 return newWay; 230 } 231 232 /** 233 * put all newNodes into the same relation(s) that originalNode is in 234 */ 235 private void fixRelations(Node originalNode, List<Command> cmds, List<Node> newNodes) { 236 // modify all relations containing the node 237 Relation newRel = null; 238 HashSet<String> rolesToReAdd = null; 239 for (Relation r : Main.ds.relations) { 240 if (r.deleted || r.incomplete) continue; 241 newRel = null; 242 rolesToReAdd = null; 243 for (RelationMember rm : r.members) { 244 if (rm.member instanceof Node) { 245 if (rm.member == originalNode) { 246 if (newRel == null) { 247 newRel = new Relation(r); 248 newRel.members.clear(); 249 rolesToReAdd = new HashSet<String>(); 250 } 251 rolesToReAdd.add(rm.role); 252 } 253 } 254 } 255 if (newRel != null) { 256 for (RelationMember rm : r.members) { 257 //if (rm.member != selectedNode) { 258 newRel.members.add(rm); 259 //} 260 } 261 for (Node n : newNodes) { 262 for (String role : rolesToReAdd) { 263 newRel.members.add(new RelationMember(role, n)); 264 } 265 } 266 cmds.add(new ChangeCommand(r, newRel)); 267 } 268 } 269 } 270 271 272 /** 273 * dupe a single node into as many nodes as there are ways using it, OR 274 * 275 * dupe a single node once, and put the copy on the selected way 276 */ 277 private void unglueWays() { 278 LinkedList<Command> cmds = new LinkedList<Command>(); 279 List<Node> newNodes = new LinkedList<Node>(); 280 281 if (selectedWay == null) { 282 boolean firstway = true; 283 // modify all ways containing the nodes 284 for (Way w : Main.ds.ways) { 285 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue; 286 if (!w.nodes.contains(selectedNode)) continue; 287 if (!firstway) cmds.add(new ChangeCommand(w, modifyWay(selectedNode, w, cmds, newNodes))); 288 firstway = false; 289 } 290 } else { 291 cmds.add(new ChangeCommand(selectedWay, modifyWay(selectedNode, selectedWay, cmds, newNodes))); 292 } 293 294 fixRelations(selectedNode, cmds, newNodes); 295 296 Main.main.undoRedo.add(new SequenceCommand(tr("Dupe into {0} nodes", newNodes.size()+1), cmds)); 297 if (selectedWay == null) { // if a node has been selected, new selection is ALL nodes 298 newNodes.add(selectedNode); 299 } // if a node and a way has been selected, new selection is only the new node that was added to the selected way 300 Main.ds.setSelected(newNodes); 301 } 302 303 /** 304 * dupe all nodes that are selected, and put the copies on the selected way 305 * 306 */ 307 private void unglueWays2() { 308 LinkedList<Command> cmds = new LinkedList<Command>(); 309 List<Node> allNewNodes = new LinkedList<Node>(); 310 Way tmpWay = selectedWay; 311 312 for (Node n : selectedNodes) { 313 List<Node> newNodes = new LinkedList<Node>(); 314 tmpWay = modifyWay(n, tmpWay, cmds, newNodes); 315 fixRelations(n, cmds, newNodes); 316 allNewNodes.addAll(newNodes); 317 } 318 cmds.add(new ChangeCommand(selectedWay, tmpWay)); // only one changeCommand for a way, else garbage will happen 319 320 Main.main.undoRedo.add(new SequenceCommand(tr("Dupe {0} nodes into {1} nodes", selectedNodes.size(), selectedNodes.size()+allNewNodes.size()), cmds)); 321 Main.ds.setSelected(allNewNodes); 322 } 323 323 324 324 // Disabled because we have such a nice help text that would not be shown otherwise. 325 325 // 326 // 327 // 328 // 329 // 330 // 331 // 332 // 333 // 334 // 326 // /** 327 // * Enable the menu option if the selection looks like we could use it. 328 // */ 329 // public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 330 // setEnabled(checkSelection(newSelection) || checkSelection2(newSelection)); 331 // selectedNode = null; 332 // selectedWay = null; 333 // selectedNodes = null; 334 // } 335 335 } -
trunk/src/org/openstreetmap/josm/actions/UndoAction.java
r1084 r1169 17 17 public class UndoAction extends JosmAction { 18 18 19 20 21 22 23 24 25 26 19 /** 20 * Construct the action with "Undo" as label. 21 */ 22 public UndoAction() { 23 super(tr("Undo"), "undo", tr("Undo the last action."), 24 Shortcut.registerShortcut("system:undo", tr("Edit: {0}", tr("Undo")), KeyEvent.VK_Z, Shortcut.GROUP_MENU), true); 25 setEnabled(false); 26 } 27 27 28 29 30 31 32 33 28 public void actionPerformed(ActionEvent e) { 29 if (Main.map == null) 30 return; 31 Main.map.repaint(); 32 Main.main.undoRedo.undo(); 33 } 34 34 } -
trunk/src/org/openstreetmap/josm/actions/UnselectAllAction.java
r1084 r1169 13 13 public class UnselectAllAction extends JosmAction { 14 14 15 16 17 18 19 15 public UnselectAllAction() { 16 super(tr("Unselect All"), "unselectall", tr("Unselect all objects."), 17 Shortcut.registerShortcut("edit:unselectall", tr("Edit: {0}", tr("Unselect All")), KeyEvent.VK_U, Shortcut.GROUP_EDIT), true); 18 // this is not really GROUP_EDIT, but users really would complain if the yhad to reconfigure because we put 19 // the correct group in 20 20 21 22 23 24 25 21 // Add extra shortcut C-S-a 22 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 23 Shortcut.registerShortcut("edit:unselectallfocus", tr("Edit: {0}", tr("Unselect All (Focus)")), 24 KeyEvent.VK_A, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT).getKeyStroke(), 25 tr("Unselect All")); 26 26 27 28 29 30 31 32 33 34 35 36 37 27 // Add extra shortcut ESCAPE 28 /* 29 * FIXME: this isn't optimal. In a better world the mapmode actions 30 * would be able to capture keyboard events and react accordingly. But 31 * for now this is a reasonable approximation. 32 */ 33 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 34 Shortcut.registerShortcut("edit:unselectallescape", tr("Edit: {0}", tr("Unselect All (Escape)")), 35 KeyEvent.VK_ESCAPE, Shortcut.GROUP_DIRECT).getKeyStroke(), 36 tr("Unselect All")); 37 } 38 38 39 40 41 39 public void actionPerformed(ActionEvent e) { 40 Main.ds.setSelected(); 41 } 42 42 } -
trunk/src/org/openstreetmap/josm/actions/UploadAction.java
r1084 r1169 35 35 public class UploadAction extends JosmAction { 36 36 37 38 39 40 41 42 43 44 45 46 47 37 /** Upload Hook */ 38 public interface UploadHook { 39 /** 40 * Checks the upload. 41 * @param add The added primitives 42 * @param update The updated primitives 43 * @param delete The deleted primitives 44 * @return true, if the upload can continue 45 */ 46 public boolean checkUpload(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete); 47 } 48 48 49 50 51 52 53 54 55 56 57 58 59 49 /** 50 * The list of upload hooks. These hooks will be called one after the other 51 * when the user wants to upload data. Plugins can insert their own hooks here 52 * if they want to be able to veto an upload. 53 * 54 * Be default, the standard upload dialog is the only element in the list. 55 * Plugins should normally insert their code before that, so that the upload 56 * dialog is the last thing shown before upload really starts; on occasion 57 * however, a plugin might also want to insert something after that. 58 */ 59 public final LinkedList<UploadHook> uploadHooks = new LinkedList<UploadHook>(); 60 60 61 62 63 61 public UploadAction() { 62 super(tr("Upload to OSM ..."), "upload", tr("Upload all changes to the OSM server."), 63 Shortcut.registerShortcut("file:upload", tr("File: {0}", tr("Upload to OSM ...")), KeyEvent.VK_U, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true); 64 64 65 66 67 68 69 70 65 /** 66 * Displays a screen where the actions that would be taken are displayed and 67 * give the user the possibility to cancel the upload. 68 */ 69 uploadHooks.add(new UploadHook() { 70 public boolean checkUpload(Collection<OsmPrimitive> add, Collection<OsmPrimitive> update, Collection<OsmPrimitive> delete) { 71 71 72 72 JPanel p = new JPanel(new GridBagLayout()); 73 73 74 74 OsmPrimitivRenderer renderer = new OsmPrimitivRenderer(); 75 75 76 77 78 79 80 81 82 76 if (!add.isEmpty()) { 77 p.add(new JLabel(tr("Objects to add:")), GBC.eol()); 78 JList l = new JList(add.toArray()); 79 l.setCellRenderer(renderer); 80 l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10); 81 p.add(new JScrollPane(l), GBC.eol().fill()); 82 } 83 83 84 85 86 87 88 89 90 84 if (!update.isEmpty()) { 85 p.add(new JLabel(tr("Objects to modify:")), GBC.eol()); 86 JList l = new JList(update.toArray()); 87 l.setCellRenderer(renderer); 88 l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10); 89 p.add(new JScrollPane(l), GBC.eol().fill()); 90 } 91 91 92 93 94 95 96 97 98 92 if (!delete.isEmpty()) { 93 p.add(new JLabel(tr("Objects to delete:")), GBC.eol()); 94 JList l = new JList(delete.toArray()); 95 l.setCellRenderer(renderer); 96 l.setVisibleRowCount(l.getModel().getSize() < 6 ? l.getModel().getSize() : 10); 97 p.add(new JScrollPane(l), GBC.eol().fill()); 98 } 99 99 100 101 102 103 104 100 return JOptionPane.showConfirmDialog(Main.parent, p, tr("Upload these changes?"), 101 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; 102 } 103 }); 104 } 105 105 106 107 108 109 110 106 public void actionPerformed(ActionEvent e) { 107 if (Main.map == null) { 108 JOptionPane.showMessageDialog(Main.parent,tr("Nothing to upload. Get some data first.")); 109 return; 110 } 111 111 112 113 114 115 116 117 112 if (!Main.map.conflictDialog.conflicts.isEmpty()) { 113 JOptionPane.showMessageDialog(Main.parent,tr("There are unresolved conflicts. You have to resolve these first.")); 114 Main.map.conflictDialog.action.button.setSelected(true); 115 Main.map.conflictDialog.action.actionPerformed(null); 116 return; 117 } 118 118 119 120 121 122 123 124 125 126 127 128 129 130 131 119 final LinkedList<OsmPrimitive> add = new LinkedList<OsmPrimitive>(); 120 final LinkedList<OsmPrimitive> update = new LinkedList<OsmPrimitive>(); 121 final LinkedList<OsmPrimitive> delete = new LinkedList<OsmPrimitive>(); 122 for (OsmPrimitive osm : Main.ds.allPrimitives()) { 123 if (osm.get("josm/ignore") != null) 124 continue; 125 if (osm.id == 0 && !osm.deleted) 126 add.addLast(osm); 127 else if (osm.modified && !osm.deleted) 128 update.addLast(osm); 129 else if (osm.deleted && osm.id != 0) 130 delete.addFirst(osm); 131 } 132 132 133 134 135 136 133 if (add.isEmpty() && update.isEmpty() && delete.isEmpty()) { 134 JOptionPane.showMessageDialog(Main.parent,tr("No changes to upload.")); 135 return; 136 } 137 137 138 139 140 141 142 138 // Call all upload hooks in sequence. The upload confirmation dialog 139 // is one of these. 140 for(UploadHook hook : uploadHooks) 141 if(!hook.checkUpload(add, update, delete)) 142 return; 143 143 144 145 146 147 148 144 final OsmServerWriter server = new OsmServerWriter(); 145 final Collection<OsmPrimitive> all = new LinkedList<OsmPrimitive>(); 146 all.addAll(add); 147 all.addAll(update); 148 all.addAll(delete); 149 149 150 151 152 153 154 155 156 157 158 159 160 161 162 150 PleaseWaitRunnable uploadTask = new PleaseWaitRunnable(tr("Uploading data")){ 151 @Override protected void realRun() throws SAXException { 152 server.uploadOsm(all); 153 } 154 @Override protected void finish() { 155 Main.main.editLayer().cleanData(server.processed, !add.isEmpty()); 156 } 157 @Override protected void cancel() { 158 server.cancel(); 159 } 160 }; 161 Main.worker.execute(uploadTask); 162 } 163 163 } -
trunk/src/org/openstreetmap/josm/actions/ZoomInAction.java
r1087 r1169 12 12 public final class ZoomInAction extends JosmAction { 13 13 14 15 16 17 18 14 public ZoomInAction() { 15 super(tr("Zoom in"), "dialogs/zoomin", tr("Zoom in"), 16 Shortcut.registerShortcut("view:zoomin", tr("View: {0}", tr("Zoom in")), KeyEvent.VK_PLUS, Shortcut.GROUP_DIRECT), true); 17 setEnabled(true); 18 } 19 19 20 20 public void actionPerformed(ActionEvent e) { 21 21 if (Main.map == null) return; 22 23 24 22 double zoom = Main.map.mapView.getScale(); 23 Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), zoom * .9); 24 } 25 25 } -
trunk/src/org/openstreetmap/josm/actions/ZoomOutAction.java
r1087 r1169 12 12 public final class ZoomOutAction extends JosmAction { 13 13 14 15 16 17 18 14 public ZoomOutAction() { 15 super(tr("Zoom out"), "dialogs/zoomout", tr("Zoom out"), 16 Shortcut.registerShortcut("view:zoomout", tr("View: {0}", tr("Zoom out")), KeyEvent.VK_MINUS, Shortcut.GROUP_DIRECT), true); 17 setEnabled(true); 18 } 19 19 20 20 public void actionPerformed(ActionEvent e) { 21 21 if (Main.map == null) return; 22 23 24 22 double zoom = Main.map.mapView.getScale(); 23 Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), zoom /.9); 24 } 25 25 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioBackAction.java
r1084 r1169 15 15 public class AudioBackAction extends JosmAction { 16 16 17 17 private double amount; // note, normally negative, i.e. jump backwards in time 18 18 19 20 21 22 23 24 25 26 27 28 19 public AudioBackAction() { 20 super(tr("Back"), "audio-back", tr("Jump back."), 21 Shortcut.registerShortcut("audio:back", tr("Audio: {0}", tr("Back")), KeyEvent.VK_F6, Shortcut.GROUP_DIRECT), true); 22 try { 23 amount = - Double.parseDouble(Main.pref.get("audio.forwardbackamount","10.0")); 24 } catch (NumberFormatException e) { 25 amount = 10.0; 26 } 27 this.putValue("help", "Action/Back"); 28 } 29 29 30 31 32 33 34 35 36 37 38 39 30 public void actionPerformed(ActionEvent e) { 31 try { 32 if (AudioPlayer.playing() || AudioPlayer.paused()) 33 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position() + amount); 34 else 35 MarkerLayer.playAudio(); 36 } catch (Exception ex) { 37 AudioPlayer.audioMalfunction(ex); 38 } 39 } 40 40 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioFastSlowAction.java
r1084 r1169 11 11 abstract public class AudioFastSlowAction extends JosmAction { 12 12 13 13 private double multiplier; 14 14 15 16 17 18 19 20 21 22 23 24 15 public AudioFastSlowAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean fast) { 16 super(name, iconName, tooltip, shortcut, true); 17 try { 18 multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3")); 19 } catch (NumberFormatException e) { 20 multiplier = 1.3; 21 } 22 if (! fast) 23 multiplier = 1.0 / multiplier; 24 } 25 25 26 27 28 29 30 31 32 33 34 35 36 26 @Deprecated 27 public AudioFastSlowAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean fast) { 28 super(name, iconName, tooltip, shortcut, modifier, true); 29 try { 30 multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3")); 31 } catch (NumberFormatException e) { 32 multiplier = 1.3; 33 } 34 if (! fast) 35 multiplier = 1.0 / multiplier; 36 } 37 37 38 39 40 41 42 43 44 45 46 47 48 38 public void actionPerformed(ActionEvent e) { 39 double speed = AudioPlayer.speed(); 40 if (speed * multiplier <= 0.1) 41 return; 42 try { 43 if (AudioPlayer.playing() || AudioPlayer.paused()) 44 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position(), speed * multiplier); 45 } catch (Exception ex) { 46 AudioPlayer.audioMalfunction(ex); 47 } 48 } 49 49 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioFasterAction.java
r1084 r1169 9 9 public class AudioFasterAction extends AudioFastSlowAction { 10 10 11 12 13 14 11 public AudioFasterAction() { 12 super(tr("Faster"), "audio-faster", tr("Faster Forward"), 13 Shortcut.registerShortcut("audio:faster", tr("Audio: {0}", tr("Faster")), KeyEvent.VK_F9, Shortcut.GROUP_DIRECT), true); 14 } 15 15 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioFwdAction.java
r1084 r1169 15 15 public class AudioFwdAction extends JosmAction { 16 16 17 17 private double amount; 18 18 19 20 21 22 23 24 25 26 27 19 public AudioFwdAction() { 20 super(tr("Forward"), "audio-fwd", tr("Jump forward"), 21 Shortcut.registerShortcut("audio:forward", tr("Audio: {0}", tr("Forward")), KeyEvent.VK_F7, Shortcut.GROUP_DIRECT), true); 22 try { 23 amount = Double.parseDouble(Main.pref.get("audio.forwardbackamount","10.0")); 24 } catch (NumberFormatException e) { 25 amount = 10.0; 26 } 27 } 28 28 29 30 31 32 33 34 35 36 37 38 29 public void actionPerformed(ActionEvent e) { 30 try { 31 if (AudioPlayer.playing() || AudioPlayer.paused()) 32 AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position() + amount); 33 else 34 MarkerLayer.playAudio(); 35 } catch (Exception ex) { 36 AudioPlayer.audioMalfunction(ex); 37 } 38 } 39 39 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioNextAction.java
r1084 r1169 13 13 public class AudioNextAction extends JosmAction { 14 14 15 16 17 18 15 public AudioNextAction() { 16 super(tr("Next Marker"), "audio-next", tr("Play next marker."), 17 Shortcut.registerShortcut("audio:next", tr("Audio: {0}", tr("Next Marker")), KeyEvent.VK_F8, Shortcut.GROUP_DIRECT), true); 18 } 19 19 20 21 22 20 public void actionPerformed(ActionEvent e) { 21 MarkerLayer.playNextMarker(); 22 } 23 23 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioPlayPauseAction.java
r1084 r1169 15 15 public class AudioPlayPauseAction extends JosmAction { 16 16 17 18 19 20 17 public AudioPlayPauseAction() { 18 super(tr("Play/pause"), "audio-playpause", tr("Play/pause audio."), 19 Shortcut.registerShortcut("audio:pause", tr("Audio: {0}", tr("Play/pause")), KeyEvent.VK_PERIOD, Shortcut.GROUP_DIRECT), true); 20 } 21 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 22 public void actionPerformed(ActionEvent e) { 23 URL url = AudioPlayer.url(); 24 try { 25 if (AudioPlayer.paused() && url != null) { 26 AudioPlayer.play(url); 27 } else if (AudioPlayer.playing()){ 28 if (AudioPlayer.speed() != 1.0) 29 AudioPlayer.play(url, AudioPlayer.position()); 30 else 31 AudioPlayer.pause(); 32 } else { 33 // find first audio marker to play 34 MarkerLayer.playAudio(); 35 } 36 } catch (Exception ex) { 37 AudioPlayer.audioMalfunction(ex); 38 } 39 } 40 40 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioPrevAction.java
r1084 r1169 13 13 public class AudioPrevAction extends JosmAction { 14 14 15 16 17 18 15 public AudioPrevAction() { 16 super(tr("Previous Marker"), "audio-prev", tr("Play previous marker."), 17 Shortcut.registerShortcut("audio:prev", tr("Audio: {0}", tr("Previous Marker")), KeyEvent.VK_F5, Shortcut.GROUP_DIRECT), true); 18 } 19 19 20 21 22 20 public void actionPerformed(ActionEvent e) { 21 MarkerLayer.playPreviousMarker(); 22 } 23 23 } -
trunk/src/org/openstreetmap/josm/actions/audio/AudioSlowerAction.java
r1084 r1169 9 9 public class AudioSlowerAction extends AudioFastSlowAction { 10 10 11 12 13 14 11 public AudioSlowerAction() { 12 super(tr("Slower"), "audio-slower", tr("Slower Forward"), 13 Shortcut.registerShortcut("audio:slower", tr("Audio: {0}", tr("Slower")), KeyEvent.VK_F4, Shortcut.GROUP_DIRECT), true); 14 } 15 15 } -
trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
r1148 r1169 21 21 public class DownloadGpsTask implements DownloadTask { 22 22 23 24 25 26 23 private static class Task extends PleaseWaitRunnable { 24 private BoundingBoxDownloader reader; 25 private GpxData rawData; 26 private final boolean newLayer; 27 27 28 29 30 31 32 28 public Task(boolean newLayer, BoundingBoxDownloader reader) { 29 super(tr("Downloading GPS data")); 30 this.reader = reader; 31 this.newLayer = newLayer; 32 } 33 33 34 35 36 34 @Override public void realRun() throws IOException, SAXException { 35 rawData = reader.parseRawGps(); 36 } 37 37 38 39 40 38 @Override protected void finish() { 39 if (rawData == null) 40 return; 41 41 rawData.recalculateBounds(); 42 42 Bounds b = rawData.bounds; 43 String name = b.min.lat() + " " + b.min.lon() + " x " + b.max.lat() + " " + b.max.lon(); 44 GpxLayer layer = new GpxLayer(rawData, name); 45 if (newLayer || findMergeLayer() == null) 46 Main.main.addLayer(layer); 47 else 48 findMergeLayer().mergeFrom(layer); 49 } 50 51 private Layer findMergeLayer() { 52 if (Main.map == null) 53 return null; 54 Layer active = Main.map.mapView.getActiveLayer(); 55 if (active != null && active instanceof GpxLayer) 56 return active; 57 for (Layer l : Main.map.mapView.getAllLayers()) 58 if (l instanceof GpxLayer) 59 return l; 60 return null; 43 String name = b.min.lat() + " " + b.min.lon() + " x " + b.max.lat() + " " + b.max.lon(); 44 GpxLayer layer = new GpxLayer(rawData, name); 45 if (newLayer || findMergeLayer() == null) 46 Main.main.addLayer(layer); 47 else 48 findMergeLayer().mergeFrom(layer); 61 49 } 62 50 63 @Override protected void cancel() { 64 if (reader != null) 65 reader.cancel(); 66 } 67 } 51 private Layer findMergeLayer() { 52 if (Main.map == null) 53 return null; 54 Layer active = Main.map.mapView.getActiveLayer(); 55 if (active != null && active instanceof GpxLayer) 56 return active; 57 for (Layer l : Main.map.mapView.getAllLayers()) 58 if (l instanceof GpxLayer) 59 return l; 60 return null; 61 } 68 62 69 private JCheckBox checkBox = new JCheckBox(tr("Raw GPS data")); 70 71 public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) { 72 Task task = new Task(action.dialog.newLayer.isSelected(), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon)); 73 Main.worker.execute(task); 74 } 75 76 public JCheckBox getCheckBox() { 77 return checkBox; 63 @Override protected void cancel() { 64 if (reader != null) 65 reader.cancel(); 66 } 78 67 } 79 68 80 public String getPreferencesSuffix() { 81 return "gps"; 69 private JCheckBox checkBox = new JCheckBox(tr("Raw GPS data")); 70 71 public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) { 72 Task task = new Task(action.dialog.newLayer.isSelected(), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon)); 73 Main.worker.execute(task); 82 74 } 83 75 84 public void loadUrl(boolean a,java.lang.String b) { 76 public JCheckBox getCheckBox() { 77 return checkBox; 78 } 79 80 public String getPreferencesSuffix() { 81 return "gps"; 82 } 83 84 public void loadUrl(boolean a,java.lang.String b) { 85 85 // FIXME this is not currently used 86 86 } -
trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
r1146 r1169 31 31 private static Bounds currentBounds; 32 32 33 34 35 36 33 private static class Task extends PleaseWaitRunnable { 34 private OsmServerReader reader; 35 private DataSet dataSet; 36 private boolean newLayer; 37 37 38 39 40 41 42 38 public Task(boolean newLayer, OsmServerReader reader) { 39 super(tr("Downloading data")); 40 this.reader = reader; 41 this.newLayer = newLayer; 42 } 43 43 44 45 46 44 @Override public void realRun() throws IOException, SAXException { 45 dataSet = reader.parseOsm(); 46 } 47 47 48 49 50 51 52 48 @Override protected void finish() { 49 if (dataSet == null) 50 return; // user cancelled download or error occoured 51 if (dataSet.allPrimitives().isEmpty()) { 52 errorMessage = tr("No data imported."); 53 53 // need to synthesize a download bounds lest the visual indication of downloaded 54 54 // area doesn't work … … 56 56 } 57 57 58 59 60 61 62 63 58 OsmDataLayer layer = new OsmDataLayer(dataSet, tr("Data Layer"), null); 59 if (newLayer) 60 Main.main.addLayer(layer); 61 else 62 Main.main.editLayer().mergeFrom(layer); 63 } 64 64 65 66 67 68 69 70 65 @Override protected void cancel() { 66 if (reader != null) 67 reader.cancel(); 68 } 69 } 70 private JCheckBox checkBox = new JCheckBox(tr("OpenStreetMap data"), true); 71 71 72 73 74 75 76 77 78 79 80 81 82 83 72 public void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon) { 73 // Swap min and max if user has specified them the wrong way round 74 // (easy to do if you are crossing 0, for example) 75 // FIXME should perhaps be done in download dialog? 76 if (minlat > maxlat) { 77 double t = minlat; minlat = maxlat; maxlat = t; 78 } 79 if (minlon > maxlon) { 80 double t = minlon; minlon = maxlon; maxlon = t; 81 } 82 83 Task task = new Task(action != null && (action.dialog == null || action.dialog.newLayer.isSelected()), new BoundingBoxDownloader(minlat, minlon, maxlat, maxlon)); 84 84 currentBounds = new Bounds(new LatLon(minlat, minlon), new LatLon(maxlat, maxlon)); 85 85 Main.worker.execute(task); 86 86 } 87 87 88 88 public void loadUrl(boolean new_layer, String url) { 89 89 Task task = new Task(new_layer, new OsmServerLocationReader(url)); 90 90 Main.worker.execute(task); 91 91 } 92 93 94 95 92 96 public JCheckBox getCheckBox() { 97 return checkBox; 93 94 95 96 public JCheckBox getCheckBox() { 97 return checkBox; 98 98 } 99 99 100 101 100 public String getPreferencesSuffix() { 101 return "osm"; 102 102 } 103 103 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java
r1150 r1169 36 36 public class DeleteAction extends MapMode { 37 37 38 39 40 41 42 43 44 45 46 47 48 49 38 /** 39 * Construct a new DeleteAction. Mnemonic is the delete - key. 40 * @param mapFrame The frame this action belongs to. 41 */ 42 public DeleteAction(MapFrame mapFrame) { 43 super(tr("Delete Mode"), 44 "delete", 45 tr("Delete nodes or ways."), 46 Shortcut.registerShortcut("mapmode:delete", tr("Mode: {0}",tr("Delete")), KeyEvent.VK_D, Shortcut.GROUP_EDIT), 47 mapFrame, 48 ImageProvider.getCursor("normal", "delete")); 49 } 50 50 51 52 53 54 51 @Override public void enterMode() { 52 super.enterMode(); 53 Main.map.mapView.addMouseListener(this); 54 } 55 55 56 57 58 59 56 @Override public void exitMode() { 57 super.exitMode(); 58 Main.map.mapView.removeMouseListener(this); 59 } 60 60 61 61 62 63 64 65 66 67 62 @Override public void actionPerformed(ActionEvent e) { 63 super.actionPerformed(e); 64 if(!Main.map.mapView.isDrawableLayer()) 65 return; 66 doActionPerformed(e); 67 } 68 68 69 70 71 72 73 69 public void doActionPerformed(ActionEvent e) { 70 if(!Main.map.mapView.isDrawableLayer()) 71 return; 72 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 73 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 74 74 75 76 77 78 79 80 81 82 83 75 Command c; 76 if (ctrl) { 77 c = DeleteCommand.deleteWithReferences(Main.ds.getSelected()); 78 } else { 79 c = DeleteCommand.delete(Main.ds.getSelected(), !alt); 80 } 81 if (c != null) { 82 Main.main.undoRedo.add(c); 83 } 84 84 85 86 87 85 Main.ds.setSelected(); 86 Main.map.repaint(); 87 } 88 88 89 90 91 92 93 94 95 96 97 98 99 100 89 /** 90 * If user clicked with the left button, delete the nearest object. 91 * position. 92 */ 93 @Override public void mouseClicked(MouseEvent e) { 94 if (e.getButton() != MouseEvent.BUTTON1) 95 return; 96 if(!Main.map.mapView.isDrawableLayer()) 97 return; 98 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 99 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 100 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 101 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 102 OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint()); 103 Command c = null; 104 if (sel == null) { 105 WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint()); 106 if (ws != null) { 107 if (shift) { 108 c = DeleteCommand.deleteWaySegment(ws); 109 } else if (ctrl) { 110 c = DeleteCommand.deleteWithReferences(Collections.singleton((OsmPrimitive)ws.way)); 111 } else { 112 c = DeleteCommand.delete(Collections.singleton((OsmPrimitive)ws.way), !alt); 113 } 114 } 115 } else if (ctrl) { 116 c = DeleteCommand.deleteWithReferences(Collections.singleton(sel)); 117 } else { 118 c = DeleteCommand.delete(Collections.singleton(sel), !alt); 119 } 120 if (c != null) { 121 Main.main.undoRedo.add(c); 122 } 123 123 124 125 126 124 Main.ds.setSelected(); 125 Main.map.mapView.repaint(); 126 } 127 127 128 129 130 128 @Override public String getModeHelpText() { 129 return tr("Click to delete. Shift: delete way segment. Alt: don't delete unused nodes when deleting a way. Ctrl: delete referring objects."); 130 } 131 131 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java
r1084 r1169 58 58 public class DrawAction extends MapMode implements MapViewPaintable, SelectionChangedListener, AWTEventListener { 59 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 60 private static Node lastUsedNode = null; 61 private double PHI=Math.toRadians(90); 62 63 private boolean ctrl; 64 private boolean alt; 65 private boolean shift; 66 private boolean mouseOnExistingNode; 67 private boolean drawHelperLine; 68 private Point mousePos; 69 private Color selectedColor; 70 71 private Node currentBaseNode; 72 private EastNorth currentMouseEastNorth; 73 74 public DrawAction(MapFrame mapFrame) { 75 super(tr("Draw"), "node/autonode", tr("Draw nodes"), 76 Shortcut.registerShortcut("mapmode:draw", tr("Mode: {0}", tr("Draw")), KeyEvent.VK_A, Shortcut.GROUP_EDIT), 77 mapFrame, getCursor()); 78 79 // Add extra shortcut N 80 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 81 Shortcut.registerShortcut("mapmode:drawfocus", tr("Mode: Draw Focus"), KeyEvent.VK_N, Shortcut.GROUP_EDIT).getKeyStroke(), tr("Draw")); 82 83 //putValue("help", "Action/AddNode/Autnode"); 84 selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW); 85 86 drawHelperLine = Main.pref.getBoolean("draw.helper-line", true); 87 } 88 89 private static Cursor getCursor() { 90 try { 91 return ImageProvider.getCursor("crosshair", null); 92 92 } catch (Exception e) { 93 93 } 94 95 } 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 *<code>null</code> otherwise.460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 // the two segments and adjusts the node position. 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 94 return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); 95 } 96 97 @Override public void enterMode() { 98 super.enterMode(); 99 Main.map.mapView.addMouseListener(this); 100 Main.map.mapView.addMouseMotionListener(this); 101 Main.map.mapView.addTemporaryLayer(this); 102 DataSet.selListeners.add(this); 103 try { 104 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK); 105 } catch (SecurityException ex) { 106 } 107 // would like to but haven't got mouse position yet: 108 // computeHelperLine(false, false, false); 109 } 110 @Override public void exitMode() { 111 super.exitMode(); 112 Main.map.mapView.removeMouseListener(this); 113 Main.map.mapView.removeMouseMotionListener(this); 114 Main.map.mapView.removeTemporaryLayer(this); 115 DataSet.selListeners.remove(this); 116 try { 117 Toolkit.getDefaultToolkit().removeAWTEventListener(this); 118 } catch (SecurityException ex) { 119 } 120 } 121 122 /** 123 * redraw to (possibly) get rid of helper line if selection changes. 124 */ 125 public void eventDispatched(AWTEvent event) { 126 if(!Main.map.mapView.isDrawableLayer()) 127 return; 128 InputEvent e = (InputEvent) event; 129 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 130 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 131 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 132 computeHelperLine(); 133 } 134 /** 135 * redraw to (possibly) get rid of helper line if selection changes. 136 */ 137 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 138 if(!Main.map.mapView.isDrawableLayer()) 139 return; 140 computeHelperLine(); 141 } 142 143 /** 144 * If user clicked with the left button, add a node at the current mouse 145 * position. 146 * 147 * If in nodeway mode, insert the node into the way. 148 */ 149 @Override public void mouseClicked(MouseEvent e) { 150 151 if (e.getButton() != MouseEvent.BUTTON1) 152 return; 153 if(!Main.map.mapView.isDrawableLayer()) 154 return; 155 156 // we copy ctrl/alt/shift from the event just in case our global 157 // AWTEvent didn't make it through the security manager. Unclear 158 // if that can ever happen but better be safe. 159 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 160 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 161 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 162 mousePos = e.getPoint(); 163 164 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 165 Collection<Command> cmds = new LinkedList<Command>(); 166 167 ArrayList<Way> reuseWays = new ArrayList<Way>(), 168 replacedWays = new ArrayList<Way>(); 169 boolean newNode = false; 170 Node n = null; 171 172 if (!ctrl) { 173 n = Main.map.mapView.getNearestNode(mousePos); 174 } 175 176 if (n != null) { 177 // user clicked on node 178 if (shift || selection.isEmpty()) { 179 // select the clicked node and do nothing else 180 // (this is just a convenience option so that people don't 181 // have to switch modes) 182 Main.ds.setSelected(n); 183 return; 184 } 185 186 } else { 187 // no node found in clicked area 188 n = new Node(Main.map.mapView.getLatLon(e.getX(), e.getY())); 189 if (n.coor.isOutSideWorld()) { 190 JOptionPane.showMessageDialog(Main.parent, 191 tr("Cannot add a node outside of the world.")); 192 return; 193 } 194 newNode = true; 195 196 cmds.add(new AddCommand(n)); 197 198 if (!ctrl) { 199 // Insert the node into all the nearby way segments 200 List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(e.getPoint()); 201 Map<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>(); 202 for (WaySegment ws : wss) { 203 List<Integer> is; 204 if (insertPoints.containsKey(ws.way)) { 205 is = insertPoints.get(ws.way); 206 } else { 207 is = new ArrayList<Integer>(); 208 insertPoints.put(ws.way, is); 209 } 210 211 is.add(ws.lowerIndex); 212 } 213 214 Set<Pair<Node,Node>> segSet = new HashSet<Pair<Node,Node>>(); 215 216 for (Map.Entry<Way, List<Integer>> insertPoint : insertPoints.entrySet()) { 217 Way w = insertPoint.getKey(); 218 List<Integer> is = insertPoint.getValue(); 219 220 Way wnew = new Way(w); 221 222 pruneSuccsAndReverse(is); 223 for (int i : is) segSet.add( 224 Pair.sort(new Pair<Node,Node>(w.nodes.get(i), w.nodes.get(i+1)))); 225 for (int i : is) wnew.nodes.add(i + 1, n); 226 227 cmds.add(new ChangeCommand(insertPoint.getKey(), wnew)); 228 replacedWays.add(insertPoint.getKey()); 229 reuseWays.add(wnew); 230 } 231 232 adjustNode(segSet, n); 233 } 234 } 235 236 // This part decides whether or not a "segment" (i.e. a connection) is made to an 237 // existing node. 238 239 // For a connection to be made, the user must either have a node selected (connection 240 // is made to that node), or he must have a way selected *and* one of the endpoints 241 // of that way must be the last used node (connection is made to last used node), or 242 // he must have a way and a node selected (connection is made to the selected node). 243 244 boolean extendedWay = false; 245 246 if (!shift && selection.size() > 0 && selection.size() < 3) { 247 248 Node selectedNode = null; 249 Way selectedWay = null; 250 251 for (OsmPrimitive p : selection) { 252 if (p instanceof Node) { 253 if (selectedNode != null) return; 254 selectedNode = (Node) p; 255 } else if (p instanceof Way) { 256 if (selectedWay != null) return; 257 selectedWay = (Way) p; 258 } 259 } 260 261 // the node from which we make a connection 262 Node n0 = null; 263 264 if (selectedNode == null) { 265 if (selectedWay == null) return; 266 if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) { 267 n0 = lastUsedNode; 268 } 269 } else if (selectedWay == null) { 270 n0 = selectedNode; 271 } else { 272 if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) { 273 n0 = selectedNode; 274 } 275 } 276 277 if (n0 == null || n0 == n) { 278 return; // Don't create zero length way segments. 279 } 280 281 // Ok we know now that we'll insert a line segment, but will it connect to an 282 // existing way or make a new way of its own? The "alt" modifier means that the 283 // user wants a new way. 284 285 Way way = alt ? null : (selectedWay != null) ? selectedWay : getWayForNode(n0); 286 if (way == null) { 287 way = new Way(); 288 way.nodes.add(n0); 289 cmds.add(new AddCommand(way)); 290 } else { 291 int i; 292 if ((i = replacedWays.indexOf(way)) != -1) { 293 way = reuseWays.get(i); 294 } else { 295 Way wnew = new Way(way); 296 cmds.add(new ChangeCommand(way, wnew)); 297 way = wnew; 298 } 299 } 300 301 if (way.nodes.get(way.nodes.size() - 1) == n0) { 302 way.nodes.add(n); 303 } else { 304 way.nodes.add(0, n); 305 } 306 307 extendedWay = true; 308 Main.ds.setSelected(way); 309 } 310 311 String title; 312 if (!extendedWay && !newNode) { 313 return; // We didn't do anything. 314 } else if (!extendedWay) { 315 if (reuseWays.isEmpty()) { 316 title = tr("Add node"); 317 } else { 318 title = tr("Add node into way"); 319 } 320 for (Way w : reuseWays) w.selected = false; 321 Main.ds.setSelected(n); 322 } else if (!newNode) { 323 title = tr("Connect existing way to node"); 324 } else if (reuseWays.isEmpty()) { 325 title = tr("Add a new node to an existing way"); 326 } else { 327 title = tr("Add node into way and connect"); 328 } 329 330 Command c = new SequenceCommand(title, cmds); 331 332 Main.main.undoRedo.add(c); 333 lastUsedNode = n; 334 computeHelperLine(); 335 Main.map.mapView.repaint(); 336 } 337 338 @Override public void mouseMoved(MouseEvent e) { 339 if(!Main.map.mapView.isDrawableLayer()) 340 return; 341 342 // we copy ctrl/alt/shift from the event just in case our global 343 // AWTEvent didn't make it through the security manager. Unclear 344 // if that can ever happen but better be safe. 345 346 ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 347 alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 348 shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 349 mousePos = e.getPoint(); 350 351 computeHelperLine(); 352 } 353 354 /** 355 * This method prepares data required for painting the "helper line" from 356 * the last used position to the mouse cursor. It duplicates some code from 357 * mouseClicked() (FIXME). 358 */ 359 private void computeHelperLine() { 360 if (mousePos == null) { 361 // Don't draw the line. 362 currentMouseEastNorth = null; 363 currentBaseNode = null; 364 return; 365 } 366 367 double distance = -1; 368 double angle = -1; 369 370 Collection<OsmPrimitive> selection = Main.ds.getSelected(); 371 372 Node selectedNode = null; 373 Way selectedWay = null; 374 Node currentMouseNode = null; 375 mouseOnExistingNode = false; 376 377 Main.map.statusLine.setAngle(-1); 378 Main.map.statusLine.setHeading(-1); 379 Main.map.statusLine.setDist(-1); 380 381 if (!ctrl && mousePos != null) { 382 currentMouseNode = Main.map.mapView.getNearestNode(mousePos); 383 } 384 385 if (currentMouseNode != null) { 386 // user clicked on node 387 if (selection.isEmpty()) return; 388 currentMouseEastNorth = currentMouseNode.eastNorth; 389 mouseOnExistingNode = true; 390 } else { 391 // no node found in clicked area 392 currentMouseEastNorth = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y); 393 } 394 395 for (OsmPrimitive p : selection) { 396 if (p instanceof Node) { 397 if (selectedNode != null) return; 398 selectedNode = (Node) p; 399 } else if (p instanceof Way) { 400 if (selectedWay != null) return; 401 selectedWay = (Way) p; 402 } 403 } 404 405 // the node from which we make a connection 406 currentBaseNode = null; 407 Node previousNode = null; 408 409 if (selectedNode == null) { 410 if (selectedWay == null) return; 411 if (lastUsedNode == selectedWay.nodes.get(0) || lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) { 412 currentBaseNode = lastUsedNode; 413 if (lastUsedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1) && selectedWay.nodes.size() > 1) { 414 previousNode = selectedWay.nodes.get(selectedWay.nodes.size()-2); 415 } 416 } 417 } else if (selectedWay == null) { 418 currentBaseNode = selectedNode; 419 } else { 420 if (selectedNode == selectedWay.nodes.get(0) || selectedNode == selectedWay.nodes.get(selectedWay.nodes.size()-1)) { 421 currentBaseNode = selectedNode; 422 } 423 } 424 425 if (currentBaseNode == null || currentBaseNode == currentMouseNode) { 426 return; // Don't create zero length way segments. 427 } 428 429 // find out the distance, in metres, between the base point and the mouse cursor 430 LatLon mouseLatLon = Main.proj.eastNorth2latlon(currentMouseEastNorth); 431 distance = currentBaseNode.coor.greatCircleDistance(mouseLatLon); 432 double hdg = Math.toDegrees(currentBaseNode.coor.heading(mouseLatLon)); 433 if (previousNode != null) { 434 angle = hdg - Math.toDegrees(previousNode.coor.heading(currentBaseNode.coor)); 435 if (angle < 0) angle += 360; 436 } 437 Main.map.statusLine.setAngle(angle); 438 Main.map.statusLine.setHeading(hdg); 439 Main.map.statusLine.setDist(distance); 440 updateStatusLine(); 441 442 if (!drawHelperLine) return; 443 444 Main.map.mapView.repaint(); 445 } 446 447 /** 448 * Repaint on mouse exit so that the helper line goes away. 449 */ 450 @Override public void mouseExited(MouseEvent e) { 451 if(!Main.map.mapView.isDrawableLayer()) 452 return; 453 mousePos = e.getPoint(); 454 Main.map.mapView.repaint(); 455 } 456 457 /** 458 * @return If the node is the end of exactly one way, return this. 459 * <code>null</code> otherwise. 460 */ 461 public static Way getWayForNode(Node n) { 462 Way way = null; 463 for (Way w : Main.ds.ways) { 464 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue; 465 Node firstNode = w.nodes.get(0); 466 Node lastNode = w.nodes.get(w.nodes.size() - 1); 467 if ((firstNode == n || lastNode == n) && (firstNode != lastNode)) { 468 if (way != null) 469 return null; 470 way = w; 471 } 472 } 473 return way; 474 } 475 476 private static void pruneSuccsAndReverse(List<Integer> is) { 477 //if (is.size() < 2) return; 478 479 HashSet<Integer> is2 = new HashSet<Integer>(); 480 for (int i : is) { 481 if (!is2.contains(i - 1) && !is2.contains(i + 1)) { 482 is2.add(i); 483 } 484 } 485 is.clear(); 486 is.addAll(is2); 487 Collections.sort(is); 488 Collections.reverse(is); 489 } 490 491 /** 492 * Adjusts the position of a node to lie on a segment (or a segment 493 * intersection). 494 * 495 * If one or more than two segments are passed, the node is adjusted 496 * to lie on the first segment that is passed. 497 * 498 * If two segments are passed, the node is adjusted to be at their 499 * intersection. 500 * 501 * No action is taken if no segments are passed. 502 * 503 * @param segs the segments to use as a reference when adjusting 504 * @param n the node to adjust 505 */ 506 private static void adjustNode(Collection<Pair<Node,Node>> segs, Node n) { 507 508 switch (segs.size()) { 509 case 0: 510 return; 511 case 2: 512 // This computes the intersection between 513 // the two segments and adjusts the node position. 514 Iterator<Pair<Node,Node>> i = segs.iterator(); 515 Pair<Node,Node> seg = i.next(); 516 EastNorth A = seg.a.eastNorth; 517 EastNorth B = seg.b.eastNorth; 518 seg = i.next(); 519 EastNorth C = seg.a.eastNorth; 520 EastNorth D = seg.b.eastNorth; 521 522 double u=det(B.east() - A.east(), B.north() - A.north(), C.east() - D.east(), C.north() - D.north()); 523 524 // Check for parallel segments and do nothing if they are 525 // In practice this will probably only happen when a way has been duplicated 526 527 if (u == 0) return; 528 529 // q is a number between 0 and 1 530 // It is the point in the segment where the intersection occurs 531 // if the segment is scaled to lenght 1 532 533 double q = det(B.north() - C.north(), B.east() - C.east(), D.north() - C.north(), D.east() - C.east()) / u; 534 EastNorth intersection = new EastNorth( 535 B.east() + q * (A.east() - B.east()), 536 B.north() + q * (A.north() - B.north())); 537 538 int snapToIntersectionThreshold=0; 539 try { snapToIntersectionThreshold = Integer.parseInt(Main.pref.get("edit.snap-intersection-threshold","10")); } catch (NumberFormatException x) {} 540 541 // only adjust to intersection if within snapToIntersectionThreshold pixel of mouse click; otherwise 542 // fall through to default action. 543 // (for semi-parallel lines, intersection might be miles away!) 544 if (Main.map.mapView.getPoint(n.eastNorth).distance(Main.map.mapView.getPoint(intersection)) < snapToIntersectionThreshold) { 545 n.eastNorth = intersection; 546 return; 547 } 548 549 default: 550 EastNorth P = n.eastNorth; 551 seg = segs.iterator().next(); 552 A = seg.a.eastNorth; 553 B = seg.b.eastNorth; 554 double a = P.distanceSq(B); 555 double b = P.distanceSq(A); 556 double c = A.distanceSq(B); 557 557 q = (a - b + c) / (2*c); 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 558 n.eastNorth = new EastNorth( 559 B.east() + q * (A.east() - B.east()), 560 B.north() + q * (A.north() - B.north())); 561 } 562 } 563 564 // helper for adjustNode 565 static double det(double a, double b, double c, double d) 566 { 567 return a * d - b * c; 568 } 569 570 public void paint(Graphics g, MapView mv) { 571 572 // don't draw line if disabled in prefs 573 if (!drawHelperLine) return; 574 575 // sanity checks 576 if (Main.map.mapView == null) return; 577 if (mousePos == null) return; 578 579 // if shift key is held ("no auto-connect"), don't draw a line 580 if (shift) return; 581 582 // don't draw line if we don't know where from or where to 583 if (currentBaseNode == null) return; 584 if (currentMouseEastNorth == null) return; 585 586 // don't draw line if mouse is outside window 587 if (!Main.map.mapView.getBounds().contains(mousePos)) return; 588 589 Graphics2D g2 = (Graphics2D) g; 590 g2.setColor(selectedColor); 591 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 592 GeneralPath b = new GeneralPath(); 593 Point p1=mv.getPoint(currentBaseNode.eastNorth); 594 Point p2=mv.getPoint(currentMouseEastNorth); 595 596 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI; 597 598 b.moveTo(p1.x,p1.y); b.lineTo(p2.x, p2.y); 599 600 // if alt key is held ("start new way"), draw a little perpendicular line 601 if (alt) { 602 b.moveTo((int)(p1.x + 8*Math.cos(t+PHI)), (int)(p1.y + 8*Math.sin(t+PHI))); 603 b.lineTo((int)(p1.x + 8*Math.cos(t-PHI)), (int)(p1.y + 8*Math.sin(t-PHI))); 604 } 605 606 g2.draw(b); 607 g2.setStroke(new BasicStroke(1)); 608 609 } 610 611 @Override public String getModeHelpText() { 612 String rv; 613 614 if (currentBaseNode != null && !shift) { 615 if (mouseOnExistingNode) { 616 if (alt && /* FIXME: way exists */true) 617 rv = tr("Click to create a new way to the existing node."); 618 else 619 rv =tr("Click to make a connection to the existing node."); 620 } else { 621 if (alt && /* FIXME: way exists */true) 622 rv = tr("Click to insert a node and create a new way."); 623 else 624 rv = tr("Click to insert a new node and make a connection."); 625 } 626 } 627 else { 628 rv = tr("Click to insert a new node."); 629 } 630 631 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way")); 632 //rv.append(tr("Click to add a new node. Ctrl: no node re-use/auto-insert. Shift: no auto-connect. Alt: new way")); 633 return rv.toString(); 634 } 635 635 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/ExtrudeAction.java
r1158 r1169 40 40 public class ExtrudeAction extends MapMode implements MapViewPaintable { 41 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 42 enum Mode { EXTRUDE, rotate, select } 43 private Mode mode = null; 44 private long mouseDownTime = 0; 45 private WaySegment selectedSegment = null; 46 private Color selectedColor; 47 48 double xoff; 49 double yoff; 50 double distance; 51 52 /** 53 * The old cursor before the user pressed the mouse button. 54 */ 55 private Cursor oldCursor; 56 /** 57 * The current position of the mouse 58 */ 59 private Point mousePos; 60 /** 61 * The position of the mouse cursor when the drag action was initiated. 62 */ 63 private Point initialMousePos; 64 /** 65 * The time which needs to pass between click and release before something 66 * counts as a move, in milliseconds 67 */ 68 private int initialMoveDelay = 200; 69 70 /** 71 * Create a new SelectAction 72 * @param mapFrame The MapFrame this action belongs to. 73 */ 74 public ExtrudeAction(MapFrame mapFrame) { 75 super(tr("Extrude"), "extrude/extrude", tr("Create areas"), 76 Shortcut.registerShortcut("mapmode:extrude", tr("Mode: {0}", tr("Extrude")), KeyEvent.VK_X, Shortcut.GROUP_EDIT), 77 mapFrame, 78 getCursor("normal", "rectangle", Cursor.DEFAULT_CURSOR)); 79 putValue("help", "Action/Extrude/Extrude"); 80 initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200); 81 selectedColor = Main.pref.getColor(marktr("selected"), Color.YELLOW); 82 } 83 84 private static Cursor getCursor(String name, String mod, int def) { 85 try { 86 return ImageProvider.getCursor(name, mod); 87 87 } catch (Exception e) { 88 88 } 89 90 } 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 89 return Cursor.getPredefinedCursor(def); 90 } 91 92 private void setCursor(Cursor c) { 93 if (oldCursor == null) { 94 oldCursor = Main.map.mapView.getCursor(); 95 Main.map.mapView.setCursor(c); 96 } 97 } 98 99 private void restoreCursor() { 100 if (oldCursor != null) { 101 Main.map.mapView.setCursor(oldCursor); 102 oldCursor = null; 103 } 104 } 105 106 @Override public void enterMode() { 107 super.enterMode(); 108 Main.map.mapView.addMouseListener(this); 109 Main.map.mapView.addMouseMotionListener(this); 110 } 111 112 @Override public void exitMode() { 113 super.exitMode(); 114 Main.map.mapView.removeMouseListener(this); 115 Main.map.mapView.removeMouseMotionListener(this); 116 Main.map.mapView.removeTemporaryLayer(this); 117 118 } 119 120 /** 121 * If the left mouse button is pressed, move all currently selected 122 * objects (if one of them is under the mouse) or the current one under the 123 * mouse (which will become selected). 124 */ 125 @Override public void mouseDragged(MouseEvent e) { 126 if (mode == Mode.select) return; 127 128 // do not count anything as a move if it lasts less than 100 milliseconds. 129 if ((mode == Mode.EXTRUDE) && (System.currentTimeMillis() - mouseDownTime < initialMoveDelay)) return; 130 131 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) 132 return; 133 134 if (mode == Mode.EXTRUDE) { 135 setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 136 } 137 138 if (mousePos == null) { 139 mousePos = e.getPoint(); 140 return; 141 } 142 143 Main.map.mapView.repaint(); 144 mousePos = e.getPoint(); 145 146 } 147 148 public void paint(Graphics g, MapView mv) { 149 if (selectedSegment != null) { 150 Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex); 151 Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1); 152 153 EastNorth en1 = n1.eastNorth; 154 EastNorth en2 = n2.eastNorth; 155 if (en1.east() < en2.east()) { en2 = en1; en1 = n2.eastNorth; } 156 EastNorth en3 = mv.getEastNorth(mousePos.x, mousePos.y); 157 158 double u = ((en3.east()-en1.east())*(en2.east()-en1.east()) + (en3.north()-en1.north())*(en2.north()-en1.north()))/en2.distanceSq(en1); 159 // the point on the segment from which the distance to mouse pos is shortest 160 EastNorth base = new EastNorth(en1.east()+u*(en2.east()-en1.east()), en1.north()+u*(en2.north()-en1.north())); 161 162 // the distance, in projection units, between the base point and the mouse cursor 163 double len = base.distance(en3); 164 165 // find out the distance, in metres, between the base point and the mouse cursor 166 distance = Main.proj.eastNorth2latlon(base).greatCircleDistance(Main.proj.eastNorth2latlon(en3)); 167 Main.map.statusLine.setDist(distance); 168 updateStatusLine(); 169 170 // compute the angle at which the segment is drawn 171 // and use it to compute the x and y offsets for the 172 // corner points. 173 double sin_alpha = (en2.north()-en1.north())/en2.distance(en1); 174 175 // this is a kludge because sometimes extrusion just goes the wrong direction 176 if ((en3.east()>base.east()) ^ (sin_alpha < 0)) len=-len; 177 xoff = sin_alpha * len; 178 yoff = Math.sqrt(1-sin_alpha*sin_alpha) * len; 179 180 Graphics2D g2 = (Graphics2D) g; 181 g2.setColor(selectedColor); 182 g2.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 183 GeneralPath b = new GeneralPath(); 184 Point p1=mv.getPoint(en1); 185 Point p2=mv.getPoint(en2); 186 Point p3=mv.getPoint(en1.add(-xoff, -yoff)); 187 Point p4=mv.getPoint(en2.add(-xoff, -yoff)); 188 189 b.moveTo(p1.x,p1.y); b.lineTo(p3.x, p3.y); 190 b.lineTo(p4.x, p4.y); b.lineTo(p2.x, p2.y); 191 b.lineTo(p1.x,p1.y); 192 g2.draw(b); 193 g2.setStroke(new BasicStroke(1)); 194 } 195 } 196 197 /** 198 */ 199 @Override public void mousePressed(MouseEvent e) { 200 if (!(Boolean)this.getValue("active")) return; 201 if (e.getButton() != MouseEvent.BUTTON1) 202 return; 203 // boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 204 // boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 205 // boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 206 207 mouseDownTime = System.currentTimeMillis(); 208 209 selectedSegment = 210 Main.map.mapView.getNearestWaySegment(e.getPoint()); 211 212 mode = (selectedSegment == null) ? Mode.select : Mode.EXTRUDE; 213 oldCursor = Main.map.mapView.getCursor(); 214 215 updateStatusLine(); 216 Main.map.mapView.addTemporaryLayer(this); 217 Main.map.mapView.repaint(); 218 219 mousePos = e.getPoint(); 220 initialMousePos = e.getPoint(); 221 } 222 223 /** 224 * Restore the old mouse cursor. 225 */ 226 @Override public void mouseReleased(MouseEvent e) { 227 restoreCursor(); 228 if (selectedSegment == null) return; 229 if (mousePos.distance(initialMousePos) > 10) { 230 Node n1 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex); 231 Node n2 = selectedSegment.way.nodes.get(selectedSegment.lowerIndex+1); 232 EastNorth en3 = n2.eastNorth.add(-xoff, -yoff); 233 Node n3 = new Node(Main.proj.eastNorth2latlon(en3)); 234 EastNorth en4 = n1.eastNorth.add(-xoff, -yoff); 235 Node n4 = new Node(Main.proj.eastNorth2latlon(en4)); 236 Way wnew = new Way(selectedSegment.way); 237 wnew.nodes.add(selectedSegment.lowerIndex+1, n3); 238 wnew.nodes.add(selectedSegment.lowerIndex+1, n4); 239 if (wnew.nodes.size() == 4) wnew.nodes.add(n1); 240 Collection<Command> cmds = new LinkedList<Command>(); 241 cmds.add(new AddCommand(n4)); 242 cmds.add(new AddCommand(n3)); 243 cmds.add(new ChangeCommand(selectedSegment.way, wnew)); 244 Command c = new SequenceCommand(tr("Extrude Way"), cmds); 245 Main.main.undoRedo.add(c); 246 } 247 248 Main.map.mapView.removeTemporaryLayer(this); 249 selectedSegment = null; 250 mode = null; 251 updateStatusLine(); 252 Main.map.mapView.repaint(); 253 } 254 255 @Override public String getModeHelpText() { 256 if (mode == Mode.select) { 257 return tr("Release the mouse button to select the objects in the rectangle."); 258 } else if (mode == Mode.EXTRUDE) { 259 return tr("Draw a rectangle of the desired size, then release the mouse button."); 260 } else if (mode == Mode.rotate) { 261 return tr("Release the mouse button to stop rotating."); 262 } else { 263 return tr("Drag a way segment to make a rectangle."); 264 } 265 } 266 266 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/MapMode.java
r1084 r1169 23 23 */ 24 24 abstract public class MapMode extends JosmAction implements MouseListener, MouseMotionListener { 25 26 25 private final Cursor cursor; 26 private Cursor oldCursor; 27 27 28 29 30 31 32 33 34 35 28 /** 29 * Constructor for mapmodes without an menu 30 */ 31 public MapMode(String name, String iconName, String tooltip, Shortcut shortcut, MapFrame mapFrame, Cursor cursor) { 32 super(name, "mapmode/"+iconName, tooltip, shortcut, false); 33 this.cursor = cursor; 34 putValue("active", false); 35 } 36 36 37 38 39 40 41 42 43 44 45 37 /** 38 * Constructor for mapmodes without an menu 39 */ 40 @Deprecated 41 public MapMode(String name, String iconName, String tooltip, int keystroke, MapFrame mapFrame, Cursor cursor) { 42 super(name, "mapmode/"+iconName, tooltip, keystroke, 0, false); 43 this.cursor = cursor; 44 putValue("active", false); 45 } 46 46 47 48 49 50 51 52 53 54 55 47 /** 48 * Constructor for mapmodes with an menu (no shortcut will be registered) 49 */ 50 public MapMode(String name, String iconName, String tooltip, MapFrame mapFrame, Cursor cursor) { 51 putValue(NAME, name); 52 putValue(SMALL_ICON, ImageProvider.get("mapmode", iconName)); 53 putValue(SHORT_DESCRIPTION, tooltip); 54 this.cursor = cursor; 55 } 56 56 57 58 59 60 61 62 63 64 65 66 57 public void enterMode() { 58 putValue("active", true); 59 oldCursor = Main.map.mapView.getCursor(); 60 Main.map.mapView.setCursor(cursor); 61 updateStatusLine(); 62 } 63 public void exitMode() { 64 putValue("active", false); 65 Main.map.mapView.setCursor(oldCursor); 66 } 67 67 68 69 70 71 68 protected void updateStatusLine() { 69 Main.map.statusLine.setHelpText(getModeHelpText()); 70 Main.map.statusLine.repaint(); 71 } 72 72 73 74 75 76 77 78 79 80 81 82 73 public String getModeHelpText() { 74 return ""; 75 } 76 /** 77 * Call selectMapMode(this) on the parent mapFrame. 78 */ 79 public void actionPerformed(ActionEvent e) { 80 if (Main.map != null) 81 Main.map.selectMapMode(this); 82 } 83 83 84 85 86 87 88 89 90 84 public void mouseReleased(MouseEvent e) {} 85 public void mouseExited(MouseEvent e) {} 86 public void mousePressed(MouseEvent e) {} 87 public void mouseClicked(MouseEvent e) {} 88 public void mouseEntered(MouseEvent e) {} 89 public void mouseMoved(MouseEvent e) {} 90 public void mouseDragged(MouseEvent e) {} 91 91 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/PlayHeadDragMode.java
r1047 r1169 21 21 public class PlayHeadDragMode extends MapMode { 22 22 23 24 25 26 23 private boolean dragging = false; 24 private Point mousePos = null; 25 private Point mouseStart = null; 26 private PlayHeadMarker playHeadMarker = null; 27 27 28 29 30 31 32 28 public PlayHeadDragMode(PlayHeadMarker m) { 29 super("play head drag", "playheaddrag", "play head drag", null, 30 Main.map, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 31 playHeadMarker = m; 32 } 33 33 34 35 36 37 38 34 @Override public void enterMode() { 35 super.enterMode(); 36 Main.map.mapView.addMouseListener(this); 37 Main.map.mapView.addMouseMotionListener(this); 38 } 39 39 40 41 42 43 44 40 @Override public void exitMode() { 41 super.exitMode(); 42 Main.map.mapView.removeMouseListener(this); 43 Main.map.mapView.removeMouseMotionListener(this); 44 } 45 45 46 47 48 46 @Override public void mousePressed(MouseEvent ev) { 47 mouseStart = mousePos = ev.getPoint(); 48 } 49 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 50 @Override public void mouseDragged(MouseEvent ev) { 51 if (mouseStart == null || mousePos == null) return; 52 if ((ev.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) return; 53 Point p = ev.getPoint(); 54 if (p == null) return; 55 if (! dragging) { 56 if (p.distance(mouseStart) < 3) return; 57 playHeadMarker.startDrag(); 58 dragging = true; 59 } 60 if (p.distance(mousePos) == 0) return; 61 playHeadMarker.drag(Main.map.mapView.getEastNorth(ev.getX(), ev.getY())); 62 mousePos = p; 63 } 64 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 65 @Override public void mouseReleased(MouseEvent ev) { 66 Point p = ev.getPoint(); 67 mouseStart = null; 68 if (ev.getButton() != MouseEvent.BUTTON1 || p == null || ! dragging) 69 return; 70 boolean shift = (ev.getModifiers() & ActionEvent.SHIFT_MASK) != 0; 71 EastNorth en = Main.map.mapView.getEastNorth(ev.getX(), ev.getY()); 72 if (! shift) { 73 playHeadMarker.reposition(en); 74 } else { 75 playHeadMarker.synchronize(en); 76 } 77 mousePos = null; 78 dragging = false; 79 79 80 81 82 83 84 80 /* 81 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0; 82 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0; 83 */ 84 } 85 85 86 87 88 86 @Override public String getModeHelpText() { 87 return tr("Drag play head and release near track to play audio from there; SHIFT+release to synchronize audio at that point."); 88 } 89 89 } -
trunk/src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java
r1084 r1169 30 30 public class ZoomAction extends MapMode implements SelectionEnded { 31 31 32 33 34 35 36 37 38 39 40 32 /** 33 * Shortcut to the mapview. 34 */ 35 private final MapView mv; 36 /** 37 * Manager that manages the selection rectangle with the aspect ratio of the 38 * MapView. 39 */ 40 private final SelectionManager selectionManager; 41 41 42 42 43 44 45 46 47 48 49 50 51 52 53 43 /** 44 * Construct a ZoomAction without a label. 45 * @param mapFrame The MapFrame, whose zoom mode should be enabled. 46 */ 47 public ZoomAction(MapFrame mapFrame) { 48 super(tr("Zoom"), "zoom", tr("Zoom and move map"), 49 Shortcut.registerShortcut("mapmode:zoom", tr("Mode: {0}", tr("Zoom")), KeyEvent.VK_Z, Shortcut.GROUP_EDIT), 50 mapFrame, ImageProvider.getCursor("normal", "zoom")); 51 mv = mapFrame.mapView; 52 selectionManager = new SelectionManager(this, true, mv); 53 } 54 54 55 56 57 58 59 60 61 62 63 64 55 /** 56 * Zoom to the rectangle on the map. 57 */ 58 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) { 59 if (r.width >= 3 && r.height >= 3) { 60 double scale = mv.getScale() * r.getWidth()/mv.getWidth(); 61 EastNorth newCenter = mv.getEastNorth(r.x+r.width/2, r.y+r.height/2); 62 mv.zoomTo(newCenter, scale); 63 } 64 } 65 65 66 67 68 69 66 @Override public void enterMode() { 67 super.enterMode(); 68 selectionManager.register(mv); 69 } 70 70 71 72 73 74 71 @Override public void exitMode() { 72 super.exitMode(); 73 selectionManager.unregister(mv); 74 } 75 75 76 77 78 76 @Override public String getModeHelpText() { 77 return tr("Zoom by dragging or Ctrl+. or Ctrl+,; move with Ctrl+up,left,down,right; move zoom with right button"); 78 } 79 79 } -
trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java
r1149 r1169 7 7 8 8 public class PushbackTokenizer { 9 9 private PushbackReader search; 10 10 11 11 private LinkedList<String> pushBackBuf = new LinkedList<String>(); 12 12 13 14 15 13 public PushbackTokenizer(PushbackReader search) { 14 this.search = search; 15 } 16 16 17 18 19 20 21 22 23 24 25 26 27 28 17 /** 18 * The token returned is <code>null</code> or starts with an identifier character: 19 * - for an '-'. This will be the only character 20 * : for an key. The value is the next token 21 * | for "OR" 22 * ' ' for anything else. 23 * @return The next token in the stream. 24 */ 25 public String nextToken() { 26 if (!pushBackBuf.isEmpty()) { 27 return pushBackBuf.removeLast(); 28 } 29 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 } 85 30 try { 31 int next; 32 char c = ' '; 33 while (c == ' ' || c == '\t' || c == '\n') { 34 next = search.read(); 35 if (next == -1) 36 return null; 37 c = (char)next; 38 } 39 StringBuilder s; 40 switch (c) { 41 case ':': 42 next = search.read(); 43 c = (char) next; 44 if (next == -1 || c == ' ' || c == '\t') { 45 pushBack(" "); 46 } else { 47 search.unread(next); 48 } 49 return ":"; 50 case '-': 51 return "-"; 52 case '(': 53 return "("; 54 case ')': 55 return ")"; 56 case '|': 57 return "|"; 58 case '"': 59 s = new StringBuilder(" "); 60 for (int nc = search.read(); nc != -1 && nc != '"'; nc = search.read()) 61 s.append((char)nc); 62 return s.toString(); 63 default: 64 s = new StringBuilder(); 65 for (;;) { 66 s.append(c); 67 next = search.read(); 68 if (next == -1) { 69 if (s.toString().equals("OR")) 70 return "|"; 71 return " "+s.toString(); 72 } 73 c = (char)next; 74 if (c == ' ' || c == '\t' || c == '"' || c == ':' || c == '(' || c == ')' || c == '|') { 75 search.unread(next); 76 if (s.toString().equals("OR")) 77 return "|"; 78 return " "+s.toString(); 79 } 80 } 81 } 82 } catch (IOException e) { 83 throw new RuntimeException(e.getMessage(), e); 84 } 85 } 86 86 87 88 89 if (nextTok == null ? tok == null : nextTok.equals(tok)) 90 91 92 93 87 public boolean readIfEqual(String tok) { 88 String nextTok = nextToken(); 89 if (nextTok == null ? tok == null : nextTok.equals(tok)) 90 return true; 91 pushBack(nextTok); 92 return false; 93 } 94 94 95 96 97 98 99 100 101 95 public String readText() { 96 String nextTok = nextToken(); 97 if (nextTok != null && nextTok.startsWith(" ")) 98 return nextTok.substring(1); 99 pushBack(nextTok); 100 return null; 101 } 102 102 103 104 105 103 public void pushBack(String tok) { 104 pushBackBuf.addLast(tok); 105 } 106 106 } -
trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
r1168 r1169 20 20 public class SearchCompiler { 21 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 22 private boolean caseSensitive = false; 23 private PushbackTokenizer tokenizer; 24 25 public SearchCompiler(boolean caseSensitive, PushbackTokenizer tokenizer) { 26 this.caseSensitive = caseSensitive; 27 this.tokenizer = tokenizer; 28 } 29 30 abstract public static class Match { 31 abstract public boolean match(OsmPrimitive osm); 32 } 33 34 private static class Always extends Match { 35 @Override public boolean match(OsmPrimitive osm) { 36 return true; 37 } 38 } 39 40 private static class Not extends Match { 41 private final Match match; 42 public Not(Match match) {this.match = match;} 43 @Override public boolean match(OsmPrimitive osm) { 44 return !match.match(osm); 45 } 46 @Override public String toString() {return "!"+match;} 47 } 48 49 private static class And extends Match { 50 private Match lhs; 51 private Match rhs; 52 public And(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;} 53 @Override public boolean match(OsmPrimitive osm) { 54 return lhs.match(osm) && rhs.match(osm); 55 } 56 @Override public String toString() {return lhs+" && "+rhs;} 57 } 58 59 private static class Or extends Match { 60 private Match lhs; 61 private Match rhs; 62 public Or(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;} 63 @Override public boolean match(OsmPrimitive osm) { 64 return lhs.match(osm) || rhs.match(osm); 65 } 66 @Override public String toString() {return lhs+" || "+rhs;} 67 } 68 69 private static class Id extends Match { 70 private long id; 71 public Id(long id) {this.id = id;} 72 @Override public boolean match(OsmPrimitive osm) { 73 return osm.id == id; 74 } 75 @Override public String toString() {return "id="+id;} 76 } 77 78 private class KeyValue extends Match { 79 private String key; 80 private String value; 81 public KeyValue(String key, String value) {this.key = key; this.value = value; } 82 @Override public boolean match(OsmPrimitive osm) { 83 String value = null; 84 if (key.equals("timestamp")) 85 value = osm.getTimeStr(); 86 else 87 value = osm.get(key); 88 if (value == null) 89 return false; 90 String v1 = caseSensitive ? value : value.toLowerCase(); 91 String v2 = caseSensitive ? this.value : this.value.toLowerCase(); 92 // is not Java 1.5 93 //v1 = java.text.Normalizer.normalize(v1, java.text.Normalizer.Form.NFC); 94 //v2 = java.text.Normalizer.normalize(v2, java.text.Normalizer.Form.NFC); 95 return v1.indexOf(v2) != -1; 96 } 97 @Override public String toString() {return key+"="+value;} 98 } 99 100 private class Any extends Match { 101 private String s; 102 public Any(String s) {this.s = s;} 103 @Override public boolean match(OsmPrimitive osm) { 104 if (osm.keys == null) 105 return s.equals(""); 106 String search = caseSensitive ? s : s.toLowerCase(); 107 // is not Java 1.5 108 //search = java.text.Normalizer.normalize(search, java.text.Normalizer.Form.NFC); 109 for (Entry<String, String> e : osm.keys.entrySet()) { 110 String key = caseSensitive ? e.getKey() : e.getKey().toLowerCase(); 111 String value = caseSensitive ? e.getValue() : e.getValue().toLowerCase(); 112 // is not Java 1.5 113 //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC); 114 if (key.indexOf(search) != -1 || value.indexOf(search) != -1) 115 return true; 116 } 117 if (osm.user != null) { 118 String name = osm.user.name; 119 // is not Java 1.5 120 //String name = java.text.Normalizer.normalize(name, java.text.Normalizer.Form.NFC); 121 if (!caseSensitive) 122 name = name.toLowerCase(); 123 if (name.indexOf(search) != -1) 124 return true; 125 } 126 return false; 127 } 128 @Override public String toString() {return s;} 129 } 130 131 private static class ExactType extends Match { 132 private String type; 133 public ExactType(String type) {this.type = type;} 134 @Override public boolean match(OsmPrimitive osm) { 135 if (osm instanceof Node) 136 return type.equals("node"); 137 if (osm instanceof Way) 138 return type.equals("way"); 139 if (osm instanceof Relation) 140 return type.equals("relation"); 141 throw new IllegalStateException("unknown class "+osm.getClass()); 142 } 143 @Override public String toString() {return "type="+type;} 144 } 145 146 private static class UserMatch extends Match { 147 private User user; 148 public UserMatch(String user) { this.user = User.get(user); } 149 @Override public boolean match(OsmPrimitive osm) { 150 return osm.user == user; 151 } 152 @Override public String toString() { return "user=" + user.name; } 153 } 154 155 private static class NodeCount extends Match { 156 private int count; 157 public NodeCount(int count) {this.count = count;} 158 @Override public boolean match(OsmPrimitive osm) { 159 return osm instanceof Way && ((Way) osm).nodes.size() == count; 160 } 161 @Override public String toString() {return "nodes="+count;} 162 } 163 164 private static class Modified extends Match { 165 @Override public boolean match(OsmPrimitive osm) { 166 return osm.modified || osm.id == 0; 167 } 168 @Override public String toString() {return "modified";} 169 } 170 171 private static class Selected extends Match { 172 @Override public boolean match(OsmPrimitive osm) { 173 return osm.selected; 174 } 175 @Override public String toString() {return "selected";} 176 } 177 178 private static class Incomplete extends Match { 179 @Override public boolean match(OsmPrimitive osm) { 180 180 return osm.incomplete; 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 181 } 182 @Override public String toString() {return "incomplete";} 183 } 184 185 public static class ParseError extends Exception { 186 public ParseError(String msg) { 187 super(msg); 188 } 189 } 190 191 public static Match compile(String searchStr, boolean caseSensitive) 192 throws ParseError { 193 return new SearchCompiler(caseSensitive, 194 new PushbackTokenizer( 195 new PushbackReader(new StringReader(searchStr)))) 196 .parse(); 197 } 198 199 public Match parse() throws ParseError { 200 Match m = parseJuxta(); 201 if (!tokenizer.readIfEqual(null)) { 202 throw new ParseError("Unexpected token: " + tokenizer.nextToken()); 203 } 204 return m; 205 } 206 207 private Match parseJuxta() throws ParseError { 208 Match juxta = new Always(); 209 210 Match m; 211 while ((m = parseOr()) != null) { 212 juxta = new And(m, juxta); 213 } 214 215 return juxta; 216 } 217 218 private Match parseOr() throws ParseError { 219 Match a = parseNot(); 220 if (tokenizer.readIfEqual("|")) { 221 Match b = parseNot(); 222 if (a == null || b == null) { 223 throw new ParseError(tr("Missing arguments for or.")); 224 } 225 return new Or(a, b); 226 } 227 return a; 228 } 229 230 private Match parseNot() throws ParseError { 231 if (tokenizer.readIfEqual("-")) { 232 Match m = parseParens(); 233 if (m == null) { 234 throw new ParseError(tr("Missing argument for not.")); 235 } 236 return new Not(m); 237 } 238 return parseParens(); 239 } 240 241 private Match parseParens() throws ParseError { 242 if (tokenizer.readIfEqual("(")) { 243 Match m = parseJuxta(); 244 if (!tokenizer.readIfEqual(")")) { 245 throw new ParseError(tr("Expected closing parenthesis.")); 246 } 247 return m; 248 } 249 return parsePat(); 250 } 251 252 private Match parsePat() { 253 String tok = tokenizer.readText(); 254 255 if (tokenizer.readIfEqual(":")) { 256 String tok2 = tokenizer.readText(); 257 if (tok == null) tok = ""; 258 if (tok2 == null) tok2 = ""; 259 return parseKV(tok, tok2); 260 } 261 262 if (tok == null) { 263 return null; 264 } else if (tok.equals("modified")) { 265 return new Modified(); 266 } else if (tok.equals("incomplete")) { 267 return new Incomplete(); 268 } else if (tok.equals("selected")) { 269 return new Selected(); 270 } else { 271 return new Any(tok); 272 } 273 } 274 275 private Match parseKV(String key, String value) { 276 if (key.equals("type")) { 277 return new ExactType(value); 278 } else if (key.equals("user")) { 279 return new UserMatch(value); 280 } else if (key.equals("nodes")) { 281 return new NodeCount(Integer.parseInt(value)); 282 } else if (key.equals("id")) { 283 try { 284 return new Id(Long.parseLong(value)); 285 } catch (NumberFormatException x) { 286 return new Id(0); 287 } 288 } else { 289 return new KeyValue(key, value); 290 } 291 } 292 292 } -
trunk/src/org/openstreetmap/josm/actions/search/SelectionWebsiteLoader.java
r627 r1169 1 1 // License: GPL. Copyright 2007 by Immanuel Scholz and others 2 2 /** 3 * 3 * 4 4 */ 5 5 package org.openstreetmap.josm.actions.search; … … 26 26 27 27 public class SelectionWebsiteLoader extends PleaseWaitRunnable { 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 28 public final URL url; 29 public Collection<OsmPrimitive> sel; 30 private final SearchAction.SearchMode mode; 31 private OsmIdReader idReader = new OsmIdReader(); 32 public SelectionWebsiteLoader(String urlStr, SearchAction.SearchMode mode) { 33 super(tr("Load Selection")); 34 this.mode = mode; 35 URL u = null; 36 try {u = new URL(urlStr);} catch (MalformedURLException e) {} 37 this.url = u; 38 } 39 @Override protected void realRun() { 40 Main.pleaseWaitDlg.currentAction.setText(tr("Contact {0}...", url.getHost())); 41 sel = mode != SearchAction.SearchMode.remove ? new LinkedList<OsmPrimitive>() : Main.ds.allNonDeletedPrimitives(); 42 try { 43 URLConnection con = url.openConnection(); 44 InputStream in = new ProgressInputStream(con, Main.pleaseWaitDlg); 45 Main.pleaseWaitDlg.currentAction.setText(tr("Downloading...")); 46 Map<Long, String> ids = idReader.parseIds(in); 47 for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) { 48 if (ids.containsKey(osm.id) && osm.getClass().getName().toLowerCase().endsWith(ids.get(osm.id))) { 49 if (mode == SearchAction.SearchMode.remove) 50 sel.remove(osm); 51 else 52 sel.add(osm); 53 } 54 } 55 } catch (IOException e) { 56 e.printStackTrace(); 57 JOptionPane.showMessageDialog(Main.parent, tr("Could not read from url: \"{0}\"",url)); 58 } catch (SAXException e) { 59 e.printStackTrace(); 60 JOptionPane.showMessageDialog(Main.parent,tr("Parsing error in url: \"{0}\"",url)); 61 } 62 } 63 @Override protected void cancel() { 64 sel = null; 65 idReader.cancel(); 66 } 67 @Override protected void finish() { 68 if (sel != null) 69 Main.ds.setSelected(sel); 70 } 71 71 } -
trunk/src/org/openstreetmap/josm/command/AddCommand.java
r655 r1169 22 22 * A command that adds an osm primitive to a dataset. Keys cannot be added this 23 23 * way. 24 * 24 * 25 25 * See {@link ChangeCommand ChangeCommand} for comments on relation back references. 26 * 26 * 27 27 * @author imi 28 28 */ 29 29 public class AddCommand extends Command { 30 30 31 /** 32 * The primitive to add to the dataset. 33 */ 34 private final OsmPrimitive osm; 35 36 private DataSet ds; 31 /** 32 * The primitive to add to the dataset. 33 */ 34 private final OsmPrimitive osm; 37 35 38 /** 39 * Create the command and specify the element to add. 40 */ 41 public AddCommand(OsmPrimitive osm) { 42 this.osm = osm; 43 this.ds = Main.main.editLayer().data; 44 } 36 private DataSet ds; 45 37 46 @Override public boolean executeCommand() { 47 osm.visit(new AddVisitor(ds)); 48 return true; 49 } 50 51 @Override public void undoCommand() { 52 osm.visit(new DeleteVisitor(ds)); 53 } 54 55 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 56 added.add(osm); 57 } 58 59 // faster implementation 60 @Override public boolean invalidBecauselayerRemoved(Layer oldLayer) { 61 return oldLayer instanceof OsmDataLayer && ((OsmDataLayer)oldLayer).data == ds; 38 /** 39 * Create the command and specify the element to add. 40 */ 41 public AddCommand(OsmPrimitive osm) { 42 this.osm = osm; 43 this.ds = Main.main.editLayer().data; 62 44 } 63 45 64 @Override public MutableTreeNode description() { 65 NameVisitor v = new NameVisitor(); 66 osm.visit(v); 67 return new DefaultMutableTreeNode(new JLabel(tr("Add")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL)); 46 @Override public boolean executeCommand() { 47 osm.visit(new AddVisitor(ds)); 48 return true; 49 } 50 51 @Override public void undoCommand() { 52 osm.visit(new DeleteVisitor(ds)); 53 } 54 55 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 56 added.add(osm); 57 } 58 59 // faster implementation 60 @Override public boolean invalidBecauselayerRemoved(Layer oldLayer) { 61 return oldLayer instanceof OsmDataLayer && ((OsmDataLayer)oldLayer).data == ds; 62 } 63 64 @Override public MutableTreeNode description() { 65 NameVisitor v = new NameVisitor(); 66 osm.visit(v); 67 return new DefaultMutableTreeNode(new JLabel(tr("Add")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL)); 68 68 } 69 69 } -
trunk/src/org/openstreetmap/josm/command/ChangeCommand.java
r630 r1169 14 14 15 15 /** 16 * Command that basically replaces one OSM primitive by another of the 16 * Command that basically replaces one OSM primitive by another of the 17 17 * same type. 18 * 18 * 19 19 * @author Imi 20 20 */ 21 21 public class ChangeCommand extends Command { 22 22 23 24 23 private final OsmPrimitive osm; 24 private final OsmPrimitive newOsm; 25 25 26 27 28 26 public ChangeCommand(OsmPrimitive osm, OsmPrimitive newOsm) { 27 this.osm = osm; 28 this.newOsm = newOsm; 29 29 } 30 30 31 32 33 34 35 31 @Override public boolean executeCommand() { 32 super.executeCommand(); 33 osm.cloneFrom(newOsm); 34 osm.modified = true; 35 return true; 36 36 } 37 37 38 39 38 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 39 modified.add(osm); 40 40 } 41 41 42 43 44 45 42 @Override public MutableTreeNode description() { 43 NameVisitor v = new NameVisitor(); 44 osm.visit(v); 45 return new DefaultMutableTreeNode(new JLabel(tr("Change")+" "+tr(v.className)+" "+v.name, v.icon, JLabel.HORIZONTAL)); 46 46 } 47 47 } -
trunk/src/org/openstreetmap/josm/command/ChangePropertyCommand.java
r1101 r1169 20 20 * Command that manipulate the key/value structure of several objects. Manages deletion, 21 21 * adding and modify of values and keys. 22 * 22 * 23 23 * @author imi 24 24 */ 25 25 public class ChangePropertyCommand extends Command { 26 /** 27 * All primitives that are affected with this command. 28 */ 29 private final List<OsmPrimitive> objects; 30 /** 31 * The key that is subject to change. 32 */ 33 private final String key; 34 /** 35 * The key value. If it is <code>null</code>, delete all key references with the given 36 * key. Otherwise, change the properties of all objects to the given value or create keys of 37 * those objects that do not have the key yet. 38 */ 39 private final String value; 40 41 public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) { 42 this.objects = new LinkedList<OsmPrimitive>(); 43 this.key = key; 44 this.value = value; 45 if (value == null) { 46 for (OsmPrimitive osm : objects) { 47 if(osm.get(key) != null) 48 this.objects.add(osm); 49 } 50 } else { 51 for (OsmPrimitive osm : objects) { 52 String val = osm.get(key); 53 if (val == null || !value.equals(val)) { 54 this.objects.add(osm); 55 } 56 } 57 } 58 } 26 /** 27 * All primitives that are affected with this command. 28 */ 29 private final List<OsmPrimitive> objects; 30 /** 31 * The key that is subject to change. 32 */ 33 private final String key; 34 /** 35 * The key value. If it is <code>null</code>, delete all key references with the given 36 * key. Otherwise, change the properties of all objects to the given value or create keys of 37 * those objects that do not have the key yet. 38 */ 39 private final String value; 59 40 60 public ChangePropertyCommand(OsmPrimitive object, String key, String value) { 61 this.objects = new LinkedList<OsmPrimitive>(); 62 this.key = key; 63 this.value = value; 64 String val = object.get(key); 65 if ((value == null && val != null) 66 || (value != null && (val == null || !value.equals(val)))) 67 this.objects.add(object); 68 } 69 70 @Override public boolean executeCommand() { 71 super.executeCommand(); // save old 72 if (value == null) { 73 for (OsmPrimitive osm : objects) { 74 osm.modified = true; 75 osm.remove(key); 76 } 77 } else { 78 for (OsmPrimitive osm : objects) { 79 osm.modified = true; 80 osm.put(key, value); 81 } 82 } 83 return true; 84 } 41 public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) { 42 this.objects = new LinkedList<OsmPrimitive>(); 43 this.key = key; 44 this.value = value; 45 if (value == null) { 46 for (OsmPrimitive osm : objects) { 47 if(osm.get(key) != null) 48 this.objects.add(osm); 49 } 50 } else { 51 for (OsmPrimitive osm : objects) { 52 String val = osm.get(key); 53 if (val == null || !value.equals(val)) { 54 this.objects.add(osm); 55 } 56 } 57 } 58 } 85 59 86 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 87 modified.addAll(objects); 88 } 60 public ChangePropertyCommand(OsmPrimitive object, String key, String value) { 61 this.objects = new LinkedList<OsmPrimitive>(); 62 this.key = key; 63 this.value = value; 64 String val = object.get(key); 65 if ((value == null && val != null) 66 || (value != null && (val == null || !value.equals(val)))) 67 this.objects.add(object); 68 } 89 69 90 @Override public MutableTreeNode description() { 91 String text = value == null ? tr( "Remove \"{0}\" for", key) : tr("Set {0}={1} for",key,value); 92 if (objects.size() == 1) { 93 NameVisitor v = new NameVisitor(); 94 objects.iterator().next().visit(v); 95 text += " "+tr(v.className)+" "+v.name; 96 } else 97 text += " "+objects.size()+" "+trn("object","objects",objects.size()); 98 DefaultMutableTreeNode root = new DefaultMutableTreeNode(new JLabel(text, ImageProvider.get("data", "key"), JLabel.HORIZONTAL)); 99 if (objects.size() == 1) 100 return root; 101 NameVisitor v = new NameVisitor(); 102 for (OsmPrimitive osm : objects) { 103 osm.visit(v); 104 root.add(new DefaultMutableTreeNode(v.toLabel())); 105 } 106 return root; 70 @Override public boolean executeCommand() { 71 super.executeCommand(); // save old 72 if (value == null) { 73 for (OsmPrimitive osm : objects) { 74 osm.modified = true; 75 osm.remove(key); 76 } 77 } else { 78 for (OsmPrimitive osm : objects) { 79 osm.modified = true; 80 osm.put(key, value); 81 } 82 } 83 return true; 84 } 85 86 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 87 modified.addAll(objects); 88 } 89 90 @Override public MutableTreeNode description() { 91 String text = value == null ? tr( "Remove \"{0}\" for", key) : tr("Set {0}={1} for",key,value); 92 if (objects.size() == 1) { 93 NameVisitor v = new NameVisitor(); 94 objects.iterator().next().visit(v); 95 text += " "+tr(v.className)+" "+v.name; 96 } else 97 text += " "+objects.size()+" "+trn("object","objects",objects.size()); 98 DefaultMutableTreeNode root = new DefaultMutableTreeNode(new JLabel(text, ImageProvider.get("data", "key"), JLabel.HORIZONTAL)); 99 if (objects.size() == 1) 100 return root; 101 NameVisitor v = new NameVisitor(); 102 for (OsmPrimitive osm : objects) { 103 osm.visit(v); 104 root.add(new DefaultMutableTreeNode(v.toLabel())); 105 } 106 return root; 107 107 } 108 108 } -
trunk/src/org/openstreetmap/josm/command/Command.java
r655 r1169 25 25 * one atomic action on a specific dataset, such as move or delete. 26 26 * 27 * Remember that the command must be executable and undoable, even if the 27 * Remember that the command must be executable and undoable, even if the 28 28 * Main.ds has changed, so the command must save the dataset it operates on 29 29 * if necessary. … … 47 47 } 48 48 49 private CloneVisitor orig; 49 private CloneVisitor orig; 50 50 51 51 protected DataSet ds; … … 70 70 71 71 /** 72 * Undoes the command. 72 * Undoes the command. 73 73 * It can be assumed that all objects are in the same state they were before. 74 74 * It can also be assumed that executeCommand was called exactly once before. 75 * 75 * 76 76 * This implementation undoes all objects stored by a former call to executeCommand. 77 77 */ … … 122 122 * Fill in the changed data this command operates on. 123 123 * Add to the lists, don't clear them. 124 * 124 * 125 125 * @param modified The modified primitives 126 126 * @param deleted The deleted primitives -
trunk/src/org/openstreetmap/josm/command/ConflictResolveCommand.java
r630 r1169 25 25 public class ConflictResolveCommand extends Command { 26 26 27 28 29 30 27 private final Collection<ConflictItem> conflicts; 28 private final Map<OsmPrimitive, OsmPrimitive> resolved; 29 private Map<OsmPrimitive, OsmPrimitive> origAllConflicts; 30 private final ConflictDialog conflictDialog; 31 31 32 33 34 35 36 32 public ConflictResolveCommand(List<ConflictItem> conflicts, Map<OsmPrimitive, OsmPrimitive> resolved) { 33 this.conflicts = conflicts; 34 this.resolved = resolved; 35 conflictDialog = Main.map.conflictDialog; 36 } 37 37 38 39 38 @Override public boolean executeCommand() { 39 super.executeCommand(); 40 40 41 origAllConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(conflictDialog.conflicts); 42 43 Set<OsmPrimitive> completed = new HashSet<OsmPrimitive>(resolved.keySet()); 44 for (ConflictItem ci : conflicts) { 45 for (Entry<OsmPrimitive, OsmPrimitive> e : resolved.entrySet()) { 46 if (ci.resolution == ConflictResolver.Resolution.THEIR) 47 ci.apply(e.getKey(), e.getValue()); 48 else if (ci.resolution == ConflictResolver.Resolution.MY) 49 ci.apply(e.getValue(), e.getKey()); 50 else if (ci.hasConflict(e.getKey(), e.getValue())) 51 completed.remove(e.getKey()); 52 } 53 } 54 if (!completed.isEmpty()) { 55 for (OsmPrimitive k : completed) 56 conflictDialog.conflicts.remove(k); 57 conflictDialog.rebuildList(); 58 } 59 return true; 60 } 41 origAllConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(conflictDialog.conflicts); 61 42 62 @Override public void undoCommand() { 63 super.undoCommand(); 64 Main.map.conflictDialog.conflicts.clear(); 65 Main.map.conflictDialog.conflicts.putAll(origAllConflicts); 66 Main.map.conflictDialog.rebuildList(); 67 } 43 Set<OsmPrimitive> completed = new HashSet<OsmPrimitive>(resolved.keySet()); 44 for (ConflictItem ci : conflicts) { 45 for (Entry<OsmPrimitive, OsmPrimitive> e : resolved.entrySet()) { 46 if (ci.resolution == ConflictResolver.Resolution.THEIR) 47 ci.apply(e.getKey(), e.getValue()); 48 else if (ci.resolution == ConflictResolver.Resolution.MY) 49 ci.apply(e.getValue(), e.getKey()); 50 else if (ci.hasConflict(e.getKey(), e.getValue())) 51 completed.remove(e.getKey()); 52 } 53 } 54 if (!completed.isEmpty()) { 55 for (OsmPrimitive k : completed) 56 conflictDialog.conflicts.remove(k); 57 conflictDialog.rebuildList(); 58 } 59 return true; 60 } 68 61 69 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 70 modified.addAll(resolved.keySet()); 71 } 62 @Override public void undoCommand() { 63 super.undoCommand(); 64 Main.map.conflictDialog.conflicts.clear(); 65 Main.map.conflictDialog.conflicts.putAll(origAllConflicts); 66 Main.map.conflictDialog.rebuildList(); 67 } 72 68 73 @Override public MutableTreeNode description() { 74 int i = 0; 75 for (ConflictItem c : conflicts) 76 if (c.resolution != null) 77 i++; 78 return new DefaultMutableTreeNode(new JLabel(tr("Resolve {0} conflicts in {1} objects",i,resolved.size()), ImageProvider.get("data", "object"), JLabel.HORIZONTAL)); 69 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 70 modified.addAll(resolved.keySet()); 71 } 72 73 @Override public MutableTreeNode description() { 74 int i = 0; 75 for (ConflictItem c : conflicts) 76 if (c.resolution != null) 77 i++; 78 return new DefaultMutableTreeNode(new JLabel(tr("Resolve {0} conflicts in {1} objects",i,resolved.size()), ImageProvider.get("data", "object"), JLabel.HORIZONTAL)); 79 79 } 80 80 } -
trunk/src/org/openstreetmap/josm/command/DeleteCommand.java
r1047 r1169 105 105 /** 106 106 * Delete the primitives and everything they reference. 107 * 107 * 108 108 * If a node is deleted, the node and all ways and relations the node is part of are deleted as 109 109 * well. 110 * 110 * 111 111 * If a way is deleted, all relations the way is member of are also deleted. 112 * 112 * 113 113 * If a way is deleted, only the way and no nodes are deleted. 114 * 114 * 115 115 * @param selection The list of all object to be deleted. 116 116 * @return command A command to perform the deletions, or null of there is nothing to delete. … … 130 130 /** 131 131 * Try to delete all given primitives. 132 * 132 * 133 133 * If a node is used by a way, it's removed from that way. If a node or a way is used by a 134 134 * relation, inform the user and do not delete. 135 * 135 * 136 136 * If this would cause ways with less than 2 nodes to be created, delete these ways instead. If 137 137 * they are part of a relation, inform the user and do not delete. 138 * 138 * 139 139 * @param selection The objects to delete. 140 140 * @param alsoDeleteNodesInWay <code>true</code> if nodes should be deleted as well … … 341 341 // leave message in one tr() as there is a grammatical connection. 342 342 tr("You are about to delete nodes outside of the area you have downloaded." + 343 "<br>" + 343 "<br>" + 344 344 "This can cause problems because other objects (that you don't see) might use them." + 345 "<br>" + 345 "<br>" + 346 346 "Do you really want to delete?") + "</html>")); 347 347 return DontShowAgainInfo.show("delete_outside_nodes", msg, false, JOptionPane.YES_NO_OPTION, JOptionPane.YES_OPTION); -
trunk/src/org/openstreetmap/josm/command/MoveCommand.java
r655 r1169 26 26 * MoveCommand moves a set of OsmPrimitives along the map. It can be moved again 27 27 * to collect several MoveCommands into one command. 28 * 28 * 29 29 * @author imi 30 30 */ 31 31 public class MoveCommand extends Command { 32 33 34 35 36 37 * x difference movement. Coordinates are in northern/eastern 38 39 40 41 * y difference movement. Coordinates are in northern/eastern 42 43 32 /** 33 * The objects that should be moved. 34 */ 35 public Collection<Node> objects = new LinkedList<Node>(); 36 /** 37 * x difference movement. Coordinates are in northern/eastern 38 */ 39 private double x; 40 /** 41 * y difference movement. Coordinates are in northern/eastern 42 */ 43 private double y; 44 44 45 /** 46 * Small helper for holding the interesting part of the old data state of the 47 * objects. 48 */ 49 public static class OldState { 50 LatLon latlon; 51 EastNorth eastNorth; 52 boolean modified; 53 } 54 55 /** 56 * List of all old states of the objects. 57 */ 58 private List<OldState> oldState = new LinkedList<OldState>(); 45 /** 46 * Small helper for holding the interesting part of the old data state of the 47 * objects. 48 */ 49 public static class OldState { 50 LatLon latlon; 51 EastNorth eastNorth; 52 boolean modified; 53 } 59 54 60 61 public MoveCommand(OsmPrimitive osm, double x, double y) { 62 this(Collections.singleton(osm), x, y); 63 } 64 /** 65 * Create a MoveCommand and assign the initial object set and movement vector. 66 */ 67 public MoveCommand(Collection<OsmPrimitive> objects, double x, double y) { 68 this.x = x; 69 this.y = y; 70 this.objects = AllNodesVisitor.getAllNodes(objects); 71 for (Node n : this.objects) { 72 OldState os = new OldState(); 73 os.eastNorth = n.eastNorth; 74 os.latlon = n.coor; 75 os.modified = n.modified; 76 oldState.add(os); 77 } 78 } 55 /** 56 * List of all old states of the objects. 57 */ 58 private List<OldState> oldState = new LinkedList<OldState>(); 79 59 80 /**81 * Move the same set of objects again by the specified vector. The vectors82 * are added together and so the resulting will be moved to the previous83 * vector plus this one.84 *85 * The move is immediately executed and any undo will undo both vectors to86 * the original position the objects had before first moving.87 */88 public void moveAgain(double x, double y) {89 for (Node n : objects) {90 n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);91 n.coor = Main.proj.eastNorth2latlon(n.eastNorth);92 }93 this.x += x;94 this.y += y;95 }96 97 @Override public boolean executeCommand() {98 for (Node n : objects) {99 n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y);100 n.coor = Main.proj.eastNorth2latlon(n.eastNorth);101 n.modified = true;102 }103 return true;104 }105 60 106 @Override public void undoCommand() { 107 Iterator<OldState> it = oldState.iterator(); 108 for (Node n : objects) { 109 OldState os = it.next(); 110 n.eastNorth = os.eastNorth; 111 n.coor = os.latlon; 112 n.modified = os.modified; 113 } 114 } 61 public MoveCommand(OsmPrimitive osm, double x, double y) { 62 this(Collections.singleton(osm), x, y); 63 } 64 /** 65 * Create a MoveCommand and assign the initial object set and movement vector. 66 */ 67 public MoveCommand(Collection<OsmPrimitive> objects, double x, double y) { 68 this.x = x; 69 this.y = y; 70 this.objects = AllNodesVisitor.getAllNodes(objects); 71 for (Node n : this.objects) { 72 OldState os = new OldState(); 73 os.eastNorth = n.eastNorth; 74 os.latlon = n.coor; 75 os.modified = n.modified; 76 oldState.add(os); 77 } 78 } 115 79 116 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 117 for (OsmPrimitive osm : objects) 118 modified.add(osm); 119 } 80 /** 81 * Move the same set of objects again by the specified vector. The vectors 82 * are added together and so the resulting will be moved to the previous 83 * vector plus this one. 84 * 85 * The move is immediately executed and any undo will undo both vectors to 86 * the original position the objects had before first moving. 87 */ 88 public void moveAgain(double x, double y) { 89 for (Node n : objects) { 90 n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y); 91 n.coor = Main.proj.eastNorth2latlon(n.eastNorth); 92 } 93 this.x += x; 94 this.y += y; 95 } 120 96 121 @Override public MutableTreeNode description() { 122 return new DefaultMutableTreeNode(new JLabel(tr("Move")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL)); 97 @Override public boolean executeCommand() { 98 for (Node n : objects) { 99 n.eastNorth = new EastNorth(n.eastNorth.east()+x, n.eastNorth.north()+y); 100 n.coor = Main.proj.eastNorth2latlon(n.eastNorth); 101 n.modified = true; 102 } 103 return true; 104 } 105 106 @Override public void undoCommand() { 107 Iterator<OldState> it = oldState.iterator(); 108 for (Node n : objects) { 109 OldState os = it.next(); 110 n.eastNorth = os.eastNorth; 111 n.coor = os.latlon; 112 n.modified = os.modified; 113 } 114 } 115 116 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 117 for (OsmPrimitive osm : objects) 118 modified.add(osm); 119 } 120 121 @Override public MutableTreeNode description() { 122 return new DefaultMutableTreeNode(new JLabel(tr("Move")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL)); 123 123 } 124 124 } -
trunk/src/org/openstreetmap/josm/command/RotateCommand.java
r748 r1169 23 23 /** 24 24 * RotateCommand rotates a number of objects around their centre. 25 * 25 * 26 26 * @author Frederik Ramm <frederik@remote.org> 27 27 */ 28 28 public class RotateCommand extends Command { 29 30 /**31 * The objects to rotate.32 */33 public Collection<Node> objects = new LinkedList<Node>();34 35 /**36 * pivot point37 */38 private Node pivot;39 40 /**41 * angle of rotation starting click to pivot42 */43 private double startAngle;44 45 /**46 * computed rotation angle between starting click and current mouse pos47 */48 private double rotationAngle;49 50 /**51 * List of all old states of the objects.52 */53 private Map<Node, MoveCommand.OldState> oldState = new HashMap<Node, MoveCommand.OldState>();54 55 /**56 * Creates a RotateCommand.57 * Assign the initial object set, compute pivot point and rotation angle.58 * Computation of pivot point is done by the same rules that are used in59 * the "align nodes in circle" action.60 */61 public RotateCommand(Collection<OsmPrimitive> objects, EastNorth start, EastNorth end) {62 29 63 this.objects = AllNodesVisitor.getAllNodes(objects); 64 pivot = new Node(new LatLon(0,0)); 65 pivot.eastNorth = new EastNorth(0,0); 30 /** 31 * The objects to rotate. 32 */ 33 public Collection<Node> objects = new LinkedList<Node>(); 66 34 67 for (Node n : this.objects) { 68 MoveCommand.OldState os = new MoveCommand.OldState(); 69 os.eastNorth = n.eastNorth; 70 os.latlon = n.coor; 71 os.modified = n.modified; 72 oldState.put(n, os); 73 pivot.eastNorth = new EastNorth(pivot.eastNorth.east()+os.eastNorth.east(), pivot.eastNorth.north()+os.eastNorth.north()); 74 } 75 pivot.eastNorth = new EastNorth(pivot.eastNorth.east()/this.objects.size(), pivot.eastNorth.north()/this.objects.size()); 76 pivot.coor = Main.proj.eastNorth2latlon(pivot.eastNorth); 35 /** 36 * pivot point 37 */ 38 private Node pivot; 77 39 78 rotationAngle = Math.PI/2; 79 rotateAgain(start, end); 80 } 40 /** 41 * angle of rotation starting click to pivot 42 */ 43 private double startAngle; 81 44 82 /** 83 * Rotate the same set of objects again, by the angle between given 84 * start and end nodes. Internally this is added to the existing 85 * rotation so a later undo will undo the whole rotation. 86 */ 87 public void rotateAgain(EastNorth start, EastNorth end) { 88 // compute angle 89 startAngle = Math.atan2(start.east()-pivot.eastNorth.east(), start.north()-pivot.eastNorth.north()); 90 double endAngle = Math.atan2(end.east()-pivot.eastNorth.east(), end.north()-pivot.eastNorth.north()); 91 rotationAngle += startAngle - endAngle; 92 rotateNodes(false); 93 } 45 /** 46 * computed rotation angle between starting click and current mouse pos 47 */ 48 private double rotationAngle; 94 49 95 /** 96 * Helper for actually rotationg the nodes. 97 * @param setModified - true if rotated nodes should be flagged "modified" 98 */ 99 private void rotateNodes(boolean setModified) { 100 for (Node n : objects) { 101 double cosPhi = Math.cos(rotationAngle); 102 double sinPhi = Math.sin(rotationAngle); 103 EastNorth oldEastNorth = oldState.get(n).eastNorth; 104 double x = oldEastNorth.east() - pivot.eastNorth.east(); 105 double y = oldEastNorth.north() - pivot.eastNorth.north(); 106 double nx = sinPhi * x + cosPhi * y + pivot.eastNorth.east(); 107 double ny = -cosPhi * x + sinPhi * y + pivot.eastNorth.north(); 108 n.eastNorth = new EastNorth(nx, ny); 109 n.coor = Main.proj.eastNorth2latlon(n.eastNorth); 110 if (setModified) 111 n.modified = true; 112 } 113 } 114 115 @Override public boolean executeCommand() { 116 rotateNodes(true); 117 return true; 118 } 50 /** 51 * List of all old states of the objects. 52 */ 53 private Map<Node, MoveCommand.OldState> oldState = new HashMap<Node, MoveCommand.OldState>(); 119 54 120 @Override public void undoCommand() { 121 for (Node n : objects) { 122 MoveCommand.OldState os = oldState.get(n); 123 n.eastNorth = os.eastNorth; 124 n.coor = os.latlon; 125 n.modified = os.modified; 126 } 127 } 55 /** 56 * Creates a RotateCommand. 57 * Assign the initial object set, compute pivot point and rotation angle. 58 * Computation of pivot point is done by the same rules that are used in 59 * the "align nodes in circle" action. 60 */ 61 public RotateCommand(Collection<OsmPrimitive> objects, EastNorth start, EastNorth end) { 128 62 129 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 130 for (OsmPrimitive osm : objects) 131 modified.add(osm); 132 } 63 this.objects = AllNodesVisitor.getAllNodes(objects); 64 pivot = new Node(new LatLon(0,0)); 65 pivot.eastNorth = new EastNorth(0,0); 133 66 134 @Override public MutableTreeNode description() { 135 return new DefaultMutableTreeNode(new JLabel(tr("Rotate")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL)); 67 for (Node n : this.objects) { 68 MoveCommand.OldState os = new MoveCommand.OldState(); 69 os.eastNorth = n.eastNorth; 70 os.latlon = n.coor; 71 os.modified = n.modified; 72 oldState.put(n, os); 73 pivot.eastNorth = new EastNorth(pivot.eastNorth.east()+os.eastNorth.east(), pivot.eastNorth.north()+os.eastNorth.north()); 74 } 75 pivot.eastNorth = new EastNorth(pivot.eastNorth.east()/this.objects.size(), pivot.eastNorth.north()/this.objects.size()); 76 pivot.coor = Main.proj.eastNorth2latlon(pivot.eastNorth); 77 78 rotationAngle = Math.PI/2; 79 rotateAgain(start, end); 80 } 81 82 /** 83 * Rotate the same set of objects again, by the angle between given 84 * start and end nodes. Internally this is added to the existing 85 * rotation so a later undo will undo the whole rotation. 86 */ 87 public void rotateAgain(EastNorth start, EastNorth end) { 88 // compute angle 89 startAngle = Math.atan2(start.east()-pivot.eastNorth.east(), start.north()-pivot.eastNorth.north()); 90 double endAngle = Math.atan2(end.east()-pivot.eastNorth.east(), end.north()-pivot.eastNorth.north()); 91 rotationAngle += startAngle - endAngle; 92 rotateNodes(false); 93 } 94 95 /** 96 * Helper for actually rotationg the nodes. 97 * @param setModified - true if rotated nodes should be flagged "modified" 98 */ 99 private void rotateNodes(boolean setModified) { 100 for (Node n : objects) { 101 double cosPhi = Math.cos(rotationAngle); 102 double sinPhi = Math.sin(rotationAngle); 103 EastNorth oldEastNorth = oldState.get(n).eastNorth; 104 double x = oldEastNorth.east() - pivot.eastNorth.east(); 105 double y = oldEastNorth.north() - pivot.eastNorth.north(); 106 double nx = sinPhi * x + cosPhi * y + pivot.eastNorth.east(); 107 double ny = -cosPhi * x + sinPhi * y + pivot.eastNorth.north(); 108 n.eastNorth = new EastNorth(nx, ny); 109 n.coor = Main.proj.eastNorth2latlon(n.eastNorth); 110 if (setModified) 111 n.modified = true; 112 } 113 } 114 115 @Override public boolean executeCommand() { 116 rotateNodes(true); 117 return true; 118 } 119 120 @Override public void undoCommand() { 121 for (Node n : objects) { 122 MoveCommand.OldState os = oldState.get(n); 123 n.eastNorth = os.eastNorth; 124 n.coor = os.latlon; 125 n.modified = os.modified; 126 } 127 } 128 129 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 130 for (OsmPrimitive osm : objects) 131 modified.add(osm); 132 } 133 134 @Override public MutableTreeNode description() { 135 return new DefaultMutableTreeNode(new JLabel(tr("Rotate")+" "+objects.size()+" "+trn("node","nodes",objects.size()), ImageProvider.get("data", "node"), JLabel.HORIZONTAL)); 136 136 } 137 137 } -
trunk/src/org/openstreetmap/josm/command/SequenceCommand.java
r853 r1169 20 20 public class SequenceCommand extends Command { 21 21 22 23 24 25 26 27 28 22 /** 23 * The command sequenz to be executed. 24 */ 25 private Command[] sequence; 26 private boolean sequence_complete; 27 private final String name; 28 public boolean continueOnError = false; 29 29 30 31 32 33 34 35 36 37 38 30 /** 31 * Create the command by specifying the list of commands to execute. 32 * @param sequenz The sequence that should be executed. 33 */ 34 public SequenceCommand(String name, Collection<Command> sequenz) { 35 this.name = name; 36 this.sequence = new Command[sequenz.size()]; 37 this.sequence = sequenz.toArray(this.sequence); 38 } 39 39 40 /** 41 * Convenient constructor, if the commands are known at compile time. 42 */ 43 public SequenceCommand(String name, Command... sequenz) { 44 this(name, Arrays.asList(sequenz)); 45 } 46 47 public int executed_commands = 0; 48 @Override public boolean executeCommand() { 49 for (int i=0; i < sequence.length; i++) { 50 Command c = sequence[i]; 51 boolean result = c.executeCommand(); 52 if (!result) 53 Main.debug("SequenceCommand, executing command[" + i + "] " + c + " result: " + result); 54 if (!result && !continueOnError) { 55 this.undoCommands(i-1); 56 return false; 57 } 58 } 59 sequence_complete = true; 60 return true; 61 } 40 /** 41 * Convenient constructor, if the commands are known at compile time. 42 */ 43 public SequenceCommand(String name, Command... sequenz) { 44 this(name, Arrays.asList(sequenz)); 45 } 62 46 63 public Command getLastCommand() { 64 if(sequence.length == 0) 65 return null; 66 return sequence[sequence.length-1]; 67 } 68 private void undoCommands(int start) { 69 // We probably aborted this halfway though the 70 // execution sequence because of a sub-command 71 // error. We already undid the sub-commands. 72 if (!sequence_complete) 73 return; 74 for (int i = start; i >= 0; --i) 75 sequence[i].undoCommand(); 76 } 47 public int executed_commands = 0; 48 @Override public boolean executeCommand() { 49 for (int i=0; i < sequence.length; i++) { 50 Command c = sequence[i]; 51 boolean result = c.executeCommand(); 52 if (!result) 53 Main.debug("SequenceCommand, executing command[" + i + "] " + c + " result: " + result); 54 if (!result && !continueOnError) { 55 this.undoCommands(i-1); 56 return false; 57 } 58 } 59 sequence_complete = true; 60 return true; 61 } 77 62 78 @Override public void undoCommand() { 79 this.undoCommands(sequence.length-1); 80 } 63 public Command getLastCommand() { 64 if(sequence.length == 0) 65 return null; 66 return sequence[sequence.length-1]; 67 } 68 private void undoCommands(int start) { 69 // We probably aborted this halfway though the 70 // execution sequence because of a sub-command 71 // error. We already undid the sub-commands. 72 if (!sequence_complete) 73 return; 74 for (int i = start; i >= 0; --i) 75 sequence[i].undoCommand(); 76 } 81 77 82 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 83 for (Command c : sequence) 84 c.fillModifiedData(modified, deleted, added); 85 } 78 @Override public void undoCommand() { 79 this.undoCommands(sequence.length-1); 80 } 86 81 87 @Override public MutableTreeNode description() { 88 DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Sequence")+": "+name); 89 for (Command c : sequence) 90 root.add(c.description()); 91 return root; 82 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 83 for (Command c : sequence) 84 c.fillModifiedData(modified, deleted, added); 85 } 86 87 @Override public MutableTreeNode description() { 88 DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Sequence")+": "+name); 89 for (Command c : sequence) 90 root.add(c.description()); 91 return root; 92 92 } 93 93 } -
trunk/src/org/openstreetmap/josm/corrector/CorrectionTable.java
r1001 r1169 13 13 extends JTable { 14 14 15 15 private static final int MAX_VISIBLE_LINES = 10; 16 16 17 17 public static class BoldRenderer extends JLabel implements 18 18 TableCellRenderer { 19 19 20 21 22 20 public Component getTableCellRendererComponent(JTable table, 21 Object value, boolean isSelected, boolean hasFocus, int row, 22 int column) { 23 23 24 25 24 Font f = getFont(); 25 setFont(new Font(f.getName(), f.getStyle() | Font.BOLD, f.getSize())); 26 26 27 27 setText((String)value); 28 28 29 30 31 29 return this; 30 } 31 } 32 32 33 33 private static BoldRenderer boldRenderer = null; 34 34 35 36 35 protected CorrectionTable(TM correctionTableModel) { 36 super(correctionTableModel); 37 37 38 39 38 final int correctionsSize = correctionTableModel.getCorrections().size(); 39 final int lines = correctionsSize > MAX_VISIBLE_LINES ? MAX_VISIBLE_LINES 40 40 : correctionsSize; 41 42 43 41 setPreferredScrollableViewportSize(new Dimension(400, lines 42 * getRowHeight())); 43 getColumnModel().getColumn(correctionTableModel.getApplyColumn()) 44 44 .setPreferredWidth(40); 45 46 45 setRowSelectionAllowed(false); 46 } 47 47 48 49 50 51 52 53 54 55 48 public TableCellRenderer getCellRenderer(int row, int column) { 49 if (getCorrectionTableModel().isBoldCell(row, column)) { 50 if (boldRenderer == null) 51 boldRenderer = new BoldRenderer(); 52 return boldRenderer; 53 } 54 return super.getCellRenderer(row, column); 55 } 56 56 57 57 @SuppressWarnings("unchecked") 58 58 public TM getCorrectionTableModel() { 59 60 59 return (TM)getModel(); 60 } 61 61 62 62 } -
trunk/src/org/openstreetmap/josm/corrector/CorrectionTableModel.java
r1001 r1169 12 12 AbstractTableModel { 13 13 14 15 16 14 private List<C> corrections; 15 private boolean[] apply; 16 private int applyColumn; 17 17 18 public CorrectionTableModel(List<C> corrections) { 19 super(); 20 this.corrections = corrections; 21 apply = new boolean[this.corrections.size()]; 22 Arrays.fill(apply, true); 23 applyColumn = getColumnCount() - 1; 24 } 25 26 abstract public int getColumnCount(); 27 28 abstract protected boolean isBoldCell(int row, int column); 29 abstract public String getCorrectionColumnName(int colIndex); 30 abstract public Object getCorrectionValueAt(int rowIndex, int colIndex); 31 32 public List<C> getCorrections() { 33 return corrections; 34 } 35 36 public int getApplyColumn() { 37 return applyColumn; 38 } 39 40 public boolean getApply(int i) { 41 return apply[i]; 18 public CorrectionTableModel(List<C> corrections) { 19 super(); 20 this.corrections = corrections; 21 apply = new boolean[this.corrections.size()]; 22 Arrays.fill(apply, true); 23 applyColumn = getColumnCount() - 1; 42 24 } 43 25 44 public int getRowCount() { 45 return corrections.size(); 26 abstract public int getColumnCount(); 27 28 abstract protected boolean isBoldCell(int row, int column); 29 abstract public String getCorrectionColumnName(int colIndex); 30 abstract public Object getCorrectionValueAt(int rowIndex, int colIndex); 31 32 public List<C> getCorrections() { 33 return corrections; 46 34 } 47 35 48 @Override 49 public Class<?> getColumnClass(int columnIndex) { 50 if (columnIndex == applyColumn) 51 return Boolean.class; 52 return String.class; 36 public int getApplyColumn() { 37 return applyColumn; 53 38 } 54 39 55 @Override 56 public String getColumnName(int columnIndex) { 57 if (columnIndex == applyColumn) 58 return tr("Apply?"); 59 60 return getCorrectionColumnName(columnIndex); 61 } 62 63 @Override 64 public boolean isCellEditable(int rowIndex, int columnIndex) { 65 return columnIndex == applyColumn; 40 public boolean getApply(int i) { 41 return apply[i]; 66 42 } 67 43 68 @Override 44 public int getRowCount() { 45 return corrections.size(); 46 } 47 48 @Override 49 public Class<?> getColumnClass(int columnIndex) { 50 if (columnIndex == applyColumn) 51 return Boolean.class; 52 return String.class; 53 } 54 55 @Override 56 public String getColumnName(int columnIndex) { 57 if (columnIndex == applyColumn) 58 return tr("Apply?"); 59 60 return getCorrectionColumnName(columnIndex); 61 } 62 63 @Override 64 public boolean isCellEditable(int rowIndex, int columnIndex) { 65 return columnIndex == applyColumn; 66 } 67 68 @Override 69 69 public void setValueAt(Object aValue, int rowIndex, int columnIndex) { 70 71 70 if (columnIndex == applyColumn && aValue instanceof Boolean) 71 apply[rowIndex] = (Boolean)aValue; 72 72 } 73 73 74 74 public Object getValueAt(int rowIndex, int colIndex) { 75 76 77 78 79 75 if (colIndex == applyColumn) 76 return apply[rowIndex]; 77 78 return getCorrectionValueAt(rowIndex, colIndex); 79 } 80 80 } -
trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
r1002 r1169 22 22 public class ReverseWayTagCorrector extends TagCorrector<Way> { 23 23 24 24 private static class PrefixSuffixSwitcher { 25 25 26 27 28 29 26 private final String a; 27 private final String b; 28 private final Pattern startPattern; 29 private final Pattern endPattern; 30 30 31 32 33 31 private final String SEPARATOR = "[:_]?"; 32 33 public PrefixSuffixSwitcher(String a, String b) { 34 34 this.a = a; 35 35 this.b = b; … … 40 40 SEPARATOR + "(" + a + "|" + b + ")$", 41 41 Pattern.CASE_INSENSITIVE); 42 42 } 43 43 44 45 46 47 44 public String apply(String text) { 45 Matcher m = startPattern.matcher(text); 46 if (!m.lookingAt()) 47 m = endPattern.matcher(text); 48 48 49 50 49 if (m.lookingAt()) { 50 String leftRight = m.group(1).toLowerCase(); 51 51 52 StringBuilder result = new StringBuilder(); 53 result.append(text.substring(0, m.start(1))); 54 result.append(leftRight.equals(a) ? b : a); 55 result.append(text.substring(m.end(1))); 56 57 return result.toString(); 58 } 59 return text; 60 } 61 } 52 StringBuilder result = new StringBuilder(); 53 result.append(text.substring(0, m.start(1))); 54 result.append(leftRight.equals(a) ? b : a); 55 result.append(text.substring(m.end(1))); 62 56 63 private static PrefixSuffixSwitcher[] prefixSuffixSwitchers = 64 new PrefixSuffixSwitcher[] { 65 new PrefixSuffixSwitcher("left", "right"), 66 new PrefixSuffixSwitcher("forward", "backward") 67 }; 57 return result.toString(); 58 } 59 return text; 60 } 61 } 68 62 69 @Override 70 public Collection<Command> execute(Way way) throws UserCancelException { 71 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap = 72 new HashMap<OsmPrimitive, List<TagCorrection>>(); 63 private static PrefixSuffixSwitcher[] prefixSuffixSwitchers = 64 new PrefixSuffixSwitcher[] { 65 new PrefixSuffixSwitcher("left", "right"), 66 new PrefixSuffixSwitcher("forward", "backward") 67 }; 73 68 74 ArrayList<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>(); 75 primitives.add(way); 76 primitives.addAll(way.nodes); 69 @Override 70 public Collection<Command> execute(Way way) throws UserCancelException { 71 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap = 72 new HashMap<OsmPrimitive, List<TagCorrection>>(); 77 73 78 for (OsmPrimitive primitive : primitives) { 79 tagCorrectionsMap.put(primitive, new ArrayList<TagCorrection>()); 74 ArrayList<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>(); 75 primitives.add(way); 76 primitives.addAll(way.nodes); 80 77 81 for (String key : primitive.keySet()) { 82 String newKey = key; 83 String value = primitive.get(key); 84 String newValue = value; 78 for (OsmPrimitive primitive : primitives) { 79 tagCorrectionsMap.put(primitive, new ArrayList<TagCorrection>()); 85 80 86 if (key.equals("oneway")) { 87 if (value.equals("-1")) 88 newValue = OsmUtils.trueval; 89 else { 90 Boolean boolValue = OsmUtils.getOsmBoolean(value); 91 if (boolValue != null && boolValue.booleanValue()) { 92 newValue = "-1"; 93 } 94 } 95 } else { 96 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) { 97 newKey = prefixSuffixSwitcher.apply(key); 98 if (!key.equals(newKey)) 99 break; 100 } 101 } 81 for (String key : primitive.keySet()) { 82 String newKey = key; 83 String value = primitive.get(key); 84 String newValue = value; 102 85 103 if (!key.equals(newKey) || !value.equals(newValue)) 104 tagCorrectionsMap.get(primitive).add( 105 new TagCorrection(key, value, newKey, newValue)); 106 } 107 } 86 if (key.equals("oneway")) { 87 if (value.equals("-1")) 88 newValue = OsmUtils.trueval; 89 else { 90 Boolean boolValue = OsmUtils.getOsmBoolean(value); 91 if (boolValue != null && boolValue.booleanValue()) { 92 newValue = "-1"; 93 } 94 } 95 } else { 96 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) { 97 newKey = prefixSuffixSwitcher.apply(key); 98 if (!key.equals(newKey)) 99 break; 100 } 101 } 108 102 109 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap = 110 new HashMap<OsmPrimitive, List<RoleCorrection>>(); 111 roleCorrectionMap.put(way, new ArrayList<RoleCorrection>()); 103 if (!key.equals(newKey) || !value.equals(newValue)) 104 tagCorrectionsMap.get(primitive).add( 105 new TagCorrection(key, value, newKey, newValue)); 106 } 107 } 112 108 113 for (Relation relation : Main.ds.relations) { 114 for (RelationMember member : relation.members) { 115 if (!member.member.realEqual(way, true) 116 || member.role.length() == 0) 117 continue; 109 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap = 110 new HashMap<OsmPrimitive, List<RoleCorrection>>(); 111 roleCorrectionMap.put(way, new ArrayList<RoleCorrection>()); 118 112 119 boolean found = false; 120 String newRole = null; 121 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) { 122 newRole = prefixSuffixSwitcher.apply(member.role); 123 if (!newRole.equals(member.role)) { 124 found = true; 125 break; 126 } 127 } 113 for (Relation relation : Main.ds.relations) { 114 for (RelationMember member : relation.members) { 115 if (!member.member.realEqual(way, true) 116 || member.role.length() == 0) 117 continue; 128 118 129 if (found) 130 roleCorrectionMap.get(way).add( 131 new RoleCorrection(relation, member, newRole)); 132 } 133 } 119 boolean found = false; 120 String newRole = null; 121 for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) { 122 newRole = prefixSuffixSwitcher.apply(member.role); 123 if (!newRole.equals(member.role)) { 124 found = true; 125 break; 126 } 127 } 134 128 135 return applyCorrections(tagCorrectionsMap, roleCorrectionMap, 136 tr("When reverting this way, following changes to properties " 137 + "of the way and its nodes are suggested in order " 138 + "to maintain data consistency.")); 139 } 129 if (found) 130 roleCorrectionMap.get(way).add( 131 new RoleCorrection(relation, member, newRole)); 132 } 133 } 134 135 return applyCorrections(tagCorrectionsMap, roleCorrectionMap, 136 tr("When reverting this way, following changes to properties " 137 + "of the way and its nodes are suggested in order " 138 + "to maintain data consistency.")); 139 } 140 140 } -
trunk/src/org/openstreetmap/josm/corrector/TagCorrection.java
r1000 r1169 4 4 public class TagCorrection implements Correction { 5 5 6 7 8 9 6 public final String oldKey; 7 public final String newKey; 8 public final String oldValue; 9 public final String newValue; 10 10 11 11 public TagCorrection(String oldKey, String oldValue, String newKey, 12 12 String newValue) { 13 14 15 16 17 13 this.oldKey = oldKey; 14 this.oldValue = oldValue; 15 this.newKey = newKey; 16 this.newValue = newValue; 17 } 18 18 19 20 21 19 public boolean isKeyChanged() { 20 return !newKey.equals(oldKey); 21 } 22 22 23 24 25 23 public boolean isValueChanged() { 24 return !newValue.equals(oldValue); 25 } 26 26 } -
trunk/src/org/openstreetmap/josm/corrector/TagCorrectionTable.java
r1000 r1169 7 7 CorrectionTable<TagCorrectionTableModel> { 8 8 9 10 11 9 public TagCorrectionTable(List<TagCorrection> tagCorrections) { 10 super(new TagCorrectionTableModel(tagCorrections)); 11 } 12 12 13 13 } -
trunk/src/org/openstreetmap/josm/corrector/TagCorrectionTableModel.java
r1000 r1169 8 8 public class TagCorrectionTableModel extends CorrectionTableModel<TagCorrection> { 9 9 10 11 12 10 public TagCorrectionTableModel(List<TagCorrection> tagCorrections) { 11 super(tagCorrections); 12 } 13 13 14 15 16 17 14 @Override 15 public int getColumnCount() { 16 return 5; 17 } 18 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 19 @Override 20 public String getCorrectionColumnName(int colIndex) { 21 switch (colIndex) { 22 case 0: 23 return tr("Old key"); 24 case 1: 25 return tr("Old value"); 26 case 2: 27 return tr("New key"); 28 case 3: 29 return tr("New value"); 30 } 31 return null; 32 } 33 33 34 34 public Object getCorrectionValueAt(int rowIndex, int colIndex) { 35 35 TagCorrection tagCorrection = getCorrections().get(rowIndex); 36 36 37 38 39 40 41 42 43 44 45 46 47 48 37 switch (colIndex) { 38 case 0: 39 return tagCorrection.oldKey; 40 case 1: 41 return tagCorrection.oldValue; 42 case 2: 43 return tagCorrection.newKey; 44 case 3: 45 return tagCorrection.newValue; 46 } 47 return null; 48 } 49 49 50 51 52 53 54 50 protected boolean isBoldCell(int row, int column) { 51 TagCorrection tagCorrection = getCorrections().get(row); 52 return (column == 2 && tagCorrection.isKeyChanged()) 53 || (column == 3 && tagCorrection.isValueChanged()); 54 } 55 55 56 56 } -
trunk/src/org/openstreetmap/josm/corrector/TagCorrector.java
r1107 r1169 33 33 public abstract class TagCorrector<P extends OsmPrimitive> { 34 34 35 public abstract Collection<Command> execute(P primitive) 36 35 public abstract Collection<Command> execute(P primitive) 36 throws UserCancelException; 37 37 38 private String[] applicationOptions = new String[] { 39 tr("Apply selected changes"), 40 tr("Don't apply changes"), 41 tr("Cancel") 38 private String[] applicationOptions = new String[] { 39 tr("Apply selected changes"), 40 tr("Don't apply changes"), 41 tr("Cancel") 42 42 }; 43 44 protected Collection<Command> applyCorrections(45 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap,46 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap,47 String description) throws UserCancelException {48 43 49 boolean hasCorrections = false; 50 for (List<TagCorrection> tagCorrectionList : tagCorrectionsMap.values()) { 51 if (!tagCorrectionList.isEmpty()) { 52 hasCorrections = true; 53 break; 54 } 55 } 44 protected Collection<Command> applyCorrections( 45 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap, 46 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap, 47 String description) throws UserCancelException { 56 48 57 if (!hasCorrections) 58 for (List<RoleCorrection> roleCorrectionList : roleCorrectionMap 59 .values()) { 60 if (!roleCorrectionList.isEmpty()) { 61 hasCorrections = true; 62 break; 63 } 64 } 49 boolean hasCorrections = false; 50 for (List<TagCorrection> tagCorrectionList : tagCorrectionsMap.values()) { 51 if (!tagCorrectionList.isEmpty()) { 52 hasCorrections = true; 53 break; 54 } 55 } 65 56 66 if (hasCorrections) { 67 Collection<Command> commands = new ArrayList<Command>(); 68 Map<OsmPrimitive, TagCorrectionTable> tagTableMap = 69 new HashMap<OsmPrimitive, TagCorrectionTable>(); 70 Map<OsmPrimitive, RoleCorrectionTable> roleTableMap = 71 new HashMap<OsmPrimitive, RoleCorrectionTable>(); 57 if (!hasCorrections) 58 for (List<RoleCorrection> roleCorrectionList : roleCorrectionMap 59 .values()) { 60 if (!roleCorrectionList.isEmpty()) { 61 hasCorrections = true; 62 break; 63 } 64 } 72 65 73 NameVisitor nameVisitor = new NameVisitor(); 66 if (hasCorrections) { 67 Collection<Command> commands = new ArrayList<Command>(); 68 Map<OsmPrimitive, TagCorrectionTable> tagTableMap = 69 new HashMap<OsmPrimitive, TagCorrectionTable>(); 70 Map<OsmPrimitive, RoleCorrectionTable> roleTableMap = 71 new HashMap<OsmPrimitive, RoleCorrectionTable>(); 74 72 75 final JPanel p = new JPanel(new GridBagLayout());73 NameVisitor nameVisitor = new NameVisitor(); 76 74 77 final JMultilineLabel label1 = new JMultilineLabel(description); 78 label1.setMaxWidth(400); 79 p.add(label1, GBC.eop()); 75 final JPanel p = new JPanel(new GridBagLayout()); 80 76 81 final JMultilineLabel label2 = new JMultilineLabel( 82 tr("Please select which property changes you want to apply.")); 83 label2.setMaxWidth(400); 84 p.add(label2, GBC.eop()); 77 final JMultilineLabel label1 = new JMultilineLabel(description); 78 label1.setMaxWidth(400); 79 p.add(label1, GBC.eop()); 85 80 86 for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) { 87 final List<TagCorrection> tagCorrections = tagCorrectionsMap 88 .get(primitive); 81 final JMultilineLabel label2 = new JMultilineLabel( 82 tr("Please select which property changes you want to apply.")); 83 label2.setMaxWidth(400); 84 p.add(label2, GBC.eop()); 89 85 90 if (tagCorrections.isEmpty()) 91 continue; 86 for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) { 87 final List<TagCorrection> tagCorrections = tagCorrectionsMap 88 .get(primitive); 92 89 93 primitive.visit(nameVisitor); 90 if (tagCorrections.isEmpty()) 91 continue; 94 92 95 final JLabel propertiesLabel = new JLabel(tr("Properties of ")); 96 p.add(propertiesLabel, GBC.std()); 93 primitive.visit(nameVisitor); 97 94 98 final JLabel primitiveLabel = new JLabel( 99 nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT); 100 p.add(primitiveLabel, GBC.eol()); 95 final JLabel propertiesLabel = new JLabel(tr("Properties of ")); 96 p.add(propertiesLabel, GBC.std()); 101 97 102 final TagCorrectionTable table = new TagCorrectionTable( 103 tagCorrections); 104 final JScrollPane scrollPane = new JScrollPane(table); 105 p.add(scrollPane, GBC.eop()); 98 final JLabel primitiveLabel = new JLabel( 99 nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT); 100 p.add(primitiveLabel, GBC.eol()); 106 101 107 tagTableMap.put(primitive, table); 108 } 102 final TagCorrectionTable table = new TagCorrectionTable( 103 tagCorrections); 104 final JScrollPane scrollPane = new JScrollPane(table); 105 p.add(scrollPane, GBC.eop()); 109 106 110 for (OsmPrimitive primitive : roleCorrectionMap.keySet()) { 111 final List<RoleCorrection> roleCorrections = roleCorrectionMap 112 .get(primitive); 113 if (roleCorrections.isEmpty()) 114 continue; 107 tagTableMap.put(primitive, table); 108 } 115 109 116 primitive.visit(nameVisitor); 110 for (OsmPrimitive primitive : roleCorrectionMap.keySet()) { 111 final List<RoleCorrection> roleCorrections = roleCorrectionMap 112 .get(primitive); 113 if (roleCorrections.isEmpty()) 114 continue; 117 115 118 final JLabel rolesLabel = new JLabel( 119 tr("Roles in relations referring to")); 120 p.add(rolesLabel, GBC.std()); 116 primitive.visit(nameVisitor); 121 117 122 final JLabel primitiveLabel = new JLabel(123 nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT);124 p.add(primitiveLabel, GBC.eol());118 final JLabel rolesLabel = new JLabel( 119 tr("Roles in relations referring to")); 120 p.add(rolesLabel, GBC.std()); 125 121 126 final RoleCorrectionTable table = new RoleCorrectionTable( 127 roleCorrections); 128 final JScrollPane scrollPane = new JScrollPane(table); 129 p.add(scrollPane, GBC.eop()); 122 final JLabel primitiveLabel = new JLabel( 123 nameVisitor.name + ":", nameVisitor.icon, JLabel.LEFT); 124 p.add(primitiveLabel, GBC.eol()); 130 125 131 roleTableMap.put(primitive, table); 132 } 126 final RoleCorrectionTable table = new RoleCorrectionTable( 127 roleCorrections); 128 final JScrollPane scrollPane = new JScrollPane(table); 129 p.add(scrollPane, GBC.eop()); 133 130 134 int answer = JOptionPane.showOptionDialog(Main.parent, p, 131 roleTableMap.put(primitive, table); 132 } 133 134 int answer = JOptionPane.showOptionDialog(Main.parent, p, 135 135 tr("Automatic tag correction"), JOptionPane.YES_NO_CANCEL_OPTION, 136 JOptionPane.PLAIN_MESSAGE, null, 136 JOptionPane.PLAIN_MESSAGE, null, 137 137 applicationOptions, applicationOptions[0]); 138 138 139 140 141 List<TagCorrection> tagCorrections = 139 if (answer == JOptionPane.YES_OPTION) { 140 for (OsmPrimitive primitive : tagCorrectionsMap.keySet()) { 141 List<TagCorrection> tagCorrections = 142 142 tagCorrectionsMap.get(primitive); 143 143 144 144 // create the clone 145 145 OsmPrimitive clone = null; … … 147 147 else if (primitive instanceof Node) clone = new Node((Node)primitive); 148 148 else if (primitive instanceof Relation) clone = new Relation((Relation)primitive); 149 149 150 150 // use this structure to remember keys that have been set already so that 151 151 // they're not dropped by a later step 152 152 Set<String> keysChanged = new HashSet<String>(); 153 153 154 154 // apply all changes to this clone 155 156 157 158 159 160 161 162 163 155 for (int i = 0; i < tagCorrections.size(); i++) { 156 if (tagTableMap.get(primitive).getCorrectionTableModel().getApply(i)) { 157 TagCorrection tagCorrection = tagCorrections.get(i); 158 if (tagCorrection.isKeyChanged() && !keysChanged.contains(tagCorrection.oldKey)) clone.remove(tagCorrection.oldKey); 159 clone.put(tagCorrection.newKey, tagCorrection.newValue); 160 keysChanged.add(tagCorrection.newKey); 161 } 162 } 163 164 164 // save the clone 165 165 if (!keysChanged.isEmpty()) commands.add(new ChangeCommand(primitive, clone)); 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 166 } 167 for (OsmPrimitive primitive : roleCorrectionMap.keySet()) { 168 List<RoleCorrection> roleCorrections = roleCorrectionMap 169 .get(primitive); 170 for (int i = 0; i < roleCorrections.size(); i++) { 171 if (roleTableMap.get(primitive) 172 .getCorrectionTableModel().getApply(i)) { 173 RoleCorrection roleCorrection = roleCorrections 174 .get(i); 175 Relation newRelation = new Relation( 176 roleCorrection.relation); 177 for (RelationMember member : newRelation.members) 178 if (member.equals(roleCorrection.member)) 179 member.role = roleCorrection.newRole; 180 commands.add(new ChangeCommand( 181 roleCorrection.relation, newRelation)); 182 } 183 } 184 } 185 } else if (answer != JOptionPane.NO_OPTION) { 186 throw new UserCancelException(); 187 } 188 return commands; 189 } 190 190 191 192 191 return Collections.emptyList(); 192 } 193 193 } -
trunk/src/org/openstreetmap/josm/data/Bounds.java
r999 r1169 8 8 9 9 /** 10 * This is a simple data class for "rectangular" areas of the world, given in 10 * This is a simple data class for "rectangular" areas of the world, given in 11 11 * lat/lon min/max values. 12 * 12 * 13 13 * @author imi 14 14 */ 15 15 public class Bounds { 16 17 18 19 16 /** 17 * The minimum and maximum coordinates. 18 */ 19 public LatLon min, max; 20 20 21 22 23 24 25 26 27 21 /** 22 * Construct bounds out of two points 23 */ 24 public Bounds(LatLon min, LatLon max) { 25 this.min = min; 26 this.max = max; 27 } 28 28 29 /** 30 * Construct bounds that span the whole world. 31 */ 32 public Bounds() { 33 min = new LatLon(-Projection.MAX_LAT, -Projection.MAX_LON); 34 max = new LatLon(Projection.MAX_LAT, Projection.MAX_LON); 35 } 36 37 @Override public String toString() { 38 return "Bounds["+min.lat()+","+min.lon()+","+max.lat()+","+max.lon()+"]"; 39 } 29 /** 30 * Construct bounds that span the whole world. 31 */ 32 public Bounds() { 33 min = new LatLon(-Projection.MAX_LAT, -Projection.MAX_LON); 34 max = new LatLon(Projection.MAX_LAT, Projection.MAX_LON); 35 } 40 36 41 /** 42 * @return Center of the bounding box. 43 */ 44 public LatLon center() { 45 // FIXME: not sure whether this calculation is right; maybe there is some 46 // more complex calculation needed to get a center of a spherical 47 // dimension? 48 return new LatLon((min.lat()+max.lat())/2, (min.lon()+max.lon())/2); 49 } 50 51 /** 52 * Extend the bounds if necessary to include the given point. 53 */ 54 public void extend(LatLon ll) { 55 if (ll.lat() < min.lat() || ll.lon() < min.lon()) 56 min = new LatLon(Math.min(ll.lat(), min.lat()), Math.min(ll.lon(), min.lon())); 57 if (ll.lat() > max.lat() || ll.lon() > max.lon()) 58 max = new LatLon(Math.max(ll.lat(), max.lat()), Math.max(ll.lon(), max.lon())); 59 } 60 /** 61 * Is the given point within this bounds? 62 */ 63 public boolean contains(LatLon ll) { 64 if (ll.lat() < min.lat() || ll.lon() < min.lon()) 65 return false; 66 if (ll.lat() > max.lat() || ll.lon() > max.lon()) 67 return false; 68 return true; 69 } 70 71 /** 72 * Converts the lat/lon bounding box to an object of type Rectangle2D.Double 73 * @return the bounding box to Rectangle2D.Double 74 */ 75 public Rectangle2D.Double asRect() { 76 return new Rectangle2D.Double(min.lon(), min.lat(), max.lon()-min.lon(), max.lat()-min.lat()); 77 } 37 @Override public String toString() { 38 return "Bounds["+min.lat()+","+min.lon()+","+max.lat()+","+max.lon()+"]"; 39 } 40 41 /** 42 * @return Center of the bounding box. 43 */ 44 public LatLon center() { 45 // FIXME: not sure whether this calculation is right; maybe there is some 46 // more complex calculation needed to get a center of a spherical 47 // dimension? 48 return new LatLon((min.lat()+max.lat())/2, (min.lon()+max.lon())/2); 49 } 50 51 /** 52 * Extend the bounds if necessary to include the given point. 53 */ 54 public void extend(LatLon ll) { 55 if (ll.lat() < min.lat() || ll.lon() < min.lon()) 56 min = new LatLon(Math.min(ll.lat(), min.lat()), Math.min(ll.lon(), min.lon())); 57 if (ll.lat() > max.lat() || ll.lon() > max.lon()) 58 max = new LatLon(Math.max(ll.lat(), max.lat()), Math.max(ll.lon(), max.lon())); 59 } 60 /** 61 * Is the given point within this bounds? 62 */ 63 public boolean contains(LatLon ll) { 64 if (ll.lat() < min.lat() || ll.lon() < min.lon()) 65 return false; 66 if (ll.lat() > max.lat() || ll.lon() > max.lon()) 67 return false; 68 return true; 69 } 70 71 /** 72 * Converts the lat/lon bounding box to an object of type Rectangle2D.Double 73 * @return the bounding box to Rectangle2D.Double 74 */ 75 public Rectangle2D.Double asRect() { 76 return new Rectangle2D.Double(min.lon(), min.lat(), max.lon()-min.lon(), max.lat()-min.lat()); 77 } 78 78 79 79 } -
trunk/src/org/openstreetmap/josm/data/DataSetChecker.java
r627 r1169 14 14 public class DataSetChecker { 15 15 16 17 18 16 public static void check() { 17 if (Main.map == null) 18 return; 19 19 20 Set<OsmPrimitive> s = new HashSet<OsmPrimitive>(); 21 for (Layer l : Main.map.mapView.getAllLayers()) { 22 if (l instanceof OsmDataLayer) { 23 for (OsmPrimitive osm : ((OsmDataLayer)l).data.allPrimitives()) { 24 if (s.contains(osm)) { 25 JOptionPane.showMessageDialog(Main.parent, "cross references"); 26 return; 27 } 28 s.add(osm); 29 } 30 } 31 } 32 33 if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) { 34 OsmDataLayer l = (OsmDataLayer)Main.map.mapView.getActiveLayer(); 35 if (l.data != Main.ds) { 36 JOptionPane.showMessageDialog(Main.parent, "Main.ds / active layer mismatch"); 37 return; 38 } 39 } 20 Set<OsmPrimitive> s = new HashSet<OsmPrimitive>(); 21 for (Layer l : Main.map.mapView.getAllLayers()) { 22 if (l instanceof OsmDataLayer) { 23 for (OsmPrimitive osm : ((OsmDataLayer)l).data.allPrimitives()) { 24 if (s.contains(osm)) { 25 JOptionPane.showMessageDialog(Main.parent, "cross references"); 26 return; 27 } 28 s.add(osm); 29 } 30 } 31 } 40 32 41 JOptionPane.showMessageDialog(Main.parent, "working"); 42 } 33 if (Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) { 34 OsmDataLayer l = (OsmDataLayer)Main.map.mapView.getActiveLayer(); 35 if (l.data != Main.ds) { 36 JOptionPane.showMessageDialog(Main.parent, "Main.ds / active layer mismatch"); 37 return; 38 } 39 } 40 41 JOptionPane.showMessageDialog(Main.parent, "working"); 42 } 43 43 } -
trunk/src/org/openstreetmap/josm/data/Preferences.java
r1159 r1169 35 35 */ 36 36 public class Preferences { 37 38 /** 39 * Internal storage for the preferenced directory. 37 38 /** 39 * Internal storage for the preferenced directory. 40 40 * Do not access this variable directly! 41 41 * @see #getPreferencesDirFile() 42 42 */ 43 43 private File preferencesDirFile = null; 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 44 45 public static interface PreferenceChangedListener { 46 void preferenceChanged(String key, String newValue); 47 } 48 49 /** 50 * Class holding one bookmarkentry. 51 * @author imi 52 */ 53 public static class Bookmark implements Comparable<Bookmark> { 54 public String name; 55 public double[] latlon = new double[4]; // minlat, minlon, maxlat, maxlon 56 @Override public String toString() { 57 return name; 58 } 59 public int compareTo(Bookmark b) { 60 return name.toLowerCase().compareTo(b.name.toLowerCase()); 61 } 62 } 63 64 public final ArrayList<PreferenceChangedListener> listener = new ArrayList<PreferenceChangedListener>(); 65 66 /** 67 * Map the property name to the property object. 68 */ 69 protected final SortedMap<String, String> properties = new TreeMap<String, String>(); 70 protected final SortedMap<String, String> defaults = new TreeMap<String, String>(); 71 72 /** 73 * Override some values on read. This is intended to be used for technology previews 74 * where we want to temporarily modify things without changing the user's preferences 75 * file. 76 */ 77 protected static final SortedMap<String, String> override = new TreeMap<String, String>(); 78 static { 79 //override.put("osm-server.version", "0.5"); 80 //override.put("osm-server.additional-versions", ""); 81 //override.put("osm-server.url", "http://openstreetmap.gryph.de/api"); 82 //override.put("plugins", null); 83 } 84 85 /** 86 * Return the location of the user defined preferences file 87 */ 88 public String getPreferencesDir() { 89 final String path = getPreferencesDirFile().getPath(); 90 if (path.endsWith(File.separator)) 91 return path; 92 return path + File.separator; 93 } 94 94 95 95 public File getPreferencesDirFile() { … … 110 110 return preferencesDirFile; 111 111 } 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 112 113 public File getPluginsDirFile() { 114 return new File(getPreferencesDirFile(), "plugins"); 115 } 116 117 /** 118 * @return A list of all existing directories where resources could be stored. 119 */ 120 public Collection<String> getAllPossiblePreferenceDirs() { 121 LinkedList<String> locations = new LinkedList<String>(); 122 locations.add(Main.pref.getPreferencesDir()); 123 String s; 124 if ((s = System.getenv("JOSM_RESOURCES")) != null) { 125 if (!s.endsWith(File.separator)) 126 s = s + File.separator; 127 locations.add(s); 128 } 129 if ((s = System.getProperty("josm.resources")) != null) { 130 if (!s.endsWith(File.separator)) 131 s = s + File.separator; 132 locations.add(s); 133 } 134 String appdata = System.getenv("APPDATA"); 135 if (System.getenv("ALLUSERSPROFILE") != null && appdata != null 136 && appdata.lastIndexOf(File.separator) != -1) { 137 appdata = appdata.substring(appdata.lastIndexOf(File.separator)); 138 locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"), 139 appdata), "JOSM").getPath()); 140 } 141 locations.add("/usr/local/share/josm/"); 142 locations.add("/usr/local/lib/josm/"); 143 locations.add("/usr/share/josm/"); 144 locations.add("/usr/lib/josm/"); 145 return locations; 146 } 147 148 synchronized public boolean hasKey(final String key) { 149 return override.containsKey(key) ? override.get(key) != null : properties.containsKey(key); 150 } 151 152 synchronized public String get(final String key) { 153 putDefault(key, null); 154 if (override.containsKey(key)) 155 return override.get(key); 156 if (!properties.containsKey(key)) 157 return ""; 158 return properties.get(key); 159 } 160 161 synchronized public String get(final String key, final String def) { 162 putDefault(key, def); 163 if (override.containsKey(key)) 164 return override.get(key); 165 final String prop = properties.get(key); 166 if (prop == null || prop.equals("")) 167 return def; 168 return prop; 169 } 170 171 synchronized public Map<String, String> getAllPrefix(final String prefix) { 172 final Map<String,String> all = new TreeMap<String,String>(); 173 for (final Entry<String,String> e : properties.entrySet()) 174 if (e.getKey().startsWith(prefix)) 175 all.put(e.getKey(), e.getValue()); 176 for (final Entry<String,String> e : override.entrySet()) 177 if (e.getKey().startsWith(prefix)) 178 if (e.getValue() == null) 179 all.remove(e.getKey()); 180 else 181 all.put(e.getKey(), e.getValue()); 182 return all; 183 } 184 185 synchronized public Map<String, String> getDefaults() { 186 return defaults; 187 } 188 189 synchronized public void putDefault(final String key, final String def) { 190 if(!defaults.containsKey(key) || defaults.get(key) == null) 191 defaults.put(key, def); 192 else if(def != null && !defaults.get(key).equals(def)) 193 System.out.println("Defaults for " + key + " differ: " + def + " != " + defaults.get(key)); 194 } 195 196 synchronized public boolean getBoolean(final String key) { 197 putDefault(key, null); 198 if (override.containsKey(key)) 199 return override.get(key) == null ? false : Boolean.parseBoolean(override.get(key)); 200 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false; 201 } 202 203 synchronized public boolean getBoolean(final String key, final boolean def) { 204 putDefault(key, Boolean.toString(def)); 205 if (override.containsKey(key)) 206 return override.get(key) == null ? def : Boolean.parseBoolean(override.get(key)); 207 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def; 208 } 209 210 synchronized public void put(final String key, String value) { 211 String oldvalue = properties.get(key); 212 if(value != null && value.length() == 0) 213 value = null; 214 if(!((oldvalue == null && value == null) || (value != null 215 && oldvalue != null && oldvalue.equals(value)))) 216 { 217 if (value == null) 218 properties.remove(key); 219 else 220 properties.put(key, value); 221 save(); 222 firePreferenceChanged(key, value); 223 } 224 } 225 226 synchronized public void put(final String key, final boolean value) { 227 put(key, Boolean.toString(value)); 228 } 229 230 private final void firePreferenceChanged(final String key, final String value) { 231 for (final PreferenceChangedListener l : listener) 232 l.preferenceChanged(key, value); 233 } 234 235 /** 236 * Called after every put. In case of a problem, do nothing but output the error 237 * in log. 238 */ 239 public void save() { 240 try { 241 setSystemProperties(); 242 final PrintWriter out = new PrintWriter(new FileWriter(getPreferencesDir() + "preferences"), false); 243 for (final Entry<String, String> e : properties.entrySet()) { 244 out.println(e.getKey() + "=" + e.getValue()); 245 } 246 out.close(); 247 } catch (final IOException e) { 248 e.printStackTrace(); 249 // do not message anything, since this can be called from strange 250 // places. 251 } 252 } 253 254 public void load() throws IOException { 255 properties.clear(); 256 final BufferedReader in = new BufferedReader(new FileReader(getPreferencesDir()+"preferences")); 257 int lineNumber = 0; 258 ArrayList<Integer> errLines = new ArrayList<Integer>(); 259 for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++) { 260 final int i = line.indexOf('='); 261 if (i == -1 || i == 0) { 262 errLines.add(lineNumber); 263 continue; 264 } 265 properties.put(line.substring(0,i), line.substring(i+1)); 266 } 267 if (!errLines.isEmpty()) { 268 throw new IOException("Malformed config file at lines " + errLines); 269 } 270 setSystemProperties(); 271 } 272 273 public final void resetToDefault() { 274 properties.clear(); 275 properties.put("projection", "org.openstreetmap.josm.data.projection.Epsg4326"); 276 properties.put("draw.segment.direction", "true"); 277 properties.put("draw.wireframe", "false"); 278 properties.put("layerlist.visible", "true"); 279 properties.put("propertiesdialog.visible", "true"); 280 properties.put("selectionlist.visible", "true"); 281 properties.put("commandstack.visible", "true"); 282 properties.put("osm-server.url", "http://www.openstreetmap.org/api"); 283 if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") == -1) { 284 properties.put("laf", "javax.swing.plaf.metal.MetalLookAndFeel"); 285 } else { 286 properties.put("laf", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); 287 } 288 save(); 289 } 290 291 public Collection<Bookmark> loadBookmarks() throws IOException { 292 File bookmarkFile = new File(getPreferencesDir()+"bookmarks"); 293 if (!bookmarkFile.exists()) 294 bookmarkFile.createNewFile(); 295 BufferedReader in = new BufferedReader(new FileReader(bookmarkFile)); 296 297 LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>(); 298 // use pattern matches to scan text, as text may contain a "," itself 299 for (String line = in.readLine(); line != null; line = in.readLine()) { 300 Matcher m = Pattern.compile("^(.+),(-?\\d+.\\d+),(-?\\d+.\\d+),(-?\\d+.\\d+),(-?\\d+.\\d+)$").matcher(line); 301 if(m.matches()) 302 { 303 Bookmark b = new Bookmark(); 304 b.name = m.group(1); 305 for (int i = 0; i < b.latlon.length; ++i) 306 b.latlon[i] = Double.parseDouble(m.group(i+2)); 307 bookmarks.add(b); 308 } 309 } 310 in.close(); 311 Collections.sort(bookmarks); 312 return bookmarks; 313 } 314 315 public void saveBookmarks(Collection<Bookmark> bookmarks) throws IOException { 316 File bookmarkFile = new File(Main.pref.getPreferencesDir()+"bookmarks"); 317 if (!bookmarkFile.exists()) 318 bookmarkFile.createNewFile(); 319 PrintWriter out = new PrintWriter(new FileWriter(bookmarkFile)); 320 for (Bookmark b : bookmarks) { 321 out.print(b.name+","); 322 for (int i = 0; i < b.latlon.length; ++i) 323 out.print(b.latlon[i]+(i<b.latlon.length-1?",":"")); 324 out.println(); 325 } 326 out.close(); 327 } 328 329 /** 330 * Convenience method for accessing colour preferences. 331 * 332 * @param colName name of the colour 333 * @param def default value 334 * @return a Color object for the configured colour, or the default value if none configured. 335 */ 336 synchronized public Color getColor(String colName, Color def) { 337 String colStr = get("color."+colName); 338 if (colStr.equals("")) { 339 put("color."+colName, ColorHelper.color2html(def)); 340 return def; 341 } 342 return ColorHelper.html2color(colStr); 343 } 344 345 // only for compatibility. Don't use any more. 346 @Deprecated 347 public static Color getPreferencesColor(String colName, Color def) 348 { 349 return Main.pref.getColor(colName, def); 350 } 351 352 /** 353 * Convenience method for accessing colour preferences. 354 * 355 * @param colName name of the colour 356 * @param specName name of the special colour settings 357 * @param def default value 358 * @return a Color object for the configured colour, or the default value if none configured. 359 */ 360 synchronized public Color getColor(String colName, String specName, Color def) { 361 String colStr = get("color."+specName); 362 if(colStr.equals("")) 363 { 364 colStr = get("color."+colName); 365 if (colStr.equals("")) { 366 put("color."+colName, ColorHelper.color2html(def)); 367 return def; 368 } 369 } 370 putDefault("color."+colName, ColorHelper.color2html(def)); 371 return ColorHelper.html2color(colStr); 372 } 373 374 synchronized public void putColor(String colName, Color val) { 375 put("color."+colName, val != null ? ColorHelper.color2html(val) : null); 376 } 377 378 synchronized public int getInteger(String key, int def) { 379 putDefault(key, Integer.toString(def)); 380 String v = get(key); 381 if(null == v) 382 return def; 383 384 try { 385 return Integer.parseInt(v); 386 } catch(NumberFormatException e) { 387 // fall out 388 } 389 return def; 390 } 391 392 synchronized public long getLong(String key, long def) { 393 putDefault(key, Long.toString(def)); 394 String v = get(key); 395 if(null == v) 396 return def; 397 398 try { 399 return Long.parseLong(v); 400 } catch(NumberFormatException e) { 401 // fall out 402 } 403 return def; 404 } 405 406 synchronized public double getDouble(String key, double def) { 407 putDefault(key, Double.toString(def)); 408 String v = get(key); 409 if(null == v) 410 return def; 411 412 try { 413 return Double.parseDouble(v); 414 } catch(NumberFormatException e) { 415 // fall out 416 } 417 return def; 418 } 419 420 synchronized public Collection<String> getCollection(String key, Collection<String> def) { 421 String s = get(key); 422 if(s != null && s.length() != 0) 423 { 424 /* handle old comma separated stuff - remove in future */ 425 if(s.indexOf(',') >= 0) 426 return Arrays.asList(s.split(",")); 427 /* handle space separated stuff - remove in future */ 428 else if(s.indexOf(' ') >= 0) 429 return Arrays.asList(s.split(",")); 430 else 431 return Arrays.asList(s.split(";")); 432 } 433 else if(def != null) 434 return def; 435 return null; 436 } 437 synchronized public void removeFromCollection(String key, String value) { 438 ArrayList<String> a = new ArrayList<String>(getCollection(key, null)); 439 if(a != null) 440 { 441 a.remove(value); 442 putCollection(key, a); 443 } 444 } 445 synchronized public void putCollection(String key, Collection<String> val) { 446 String s = null; 447 if(val != null) 448 { 449 for(String a : val) 450 { 451 if(s != null) 452 s += ";" + a; 453 else 454 s = a; 455 } 456 } 457 458 put(key, s); 459 } 460 461 private void setSystemProperties() { 462 Properties sysProp = System.getProperties(); 463 if (getBoolean(ProxyPreferences.PROXY_ENABLE)) { 464 sysProp.put("proxySet", "true"); 465 sysProp.put("http.proxyHost", get(ProxyPreferences.PROXY_HOST)); 466 sysProp.put("proxyPort", get(ProxyPreferences.PROXY_PORT)); 467 if (!getBoolean(ProxyPreferences.PROXY_ANONYMOUS)) { 468 sysProp.put("proxyUser", get(ProxyPreferences.PROXY_USER)); 469 sysProp.put("proxyPassword", get(ProxyPreferences.PROXY_PASS)); 470 } 471 }/* else { 472 sysProp.put("proxySet", "false"); 473 sysProp.remove("http.proxyHost"); 474 sysProp.remove("proxyPort"); 475 sysProp.remove("proxyUser"); 476 sysProp.remove("proxyPassword"); 477 }*/ 478 System.setProperties(sysProp); 479 } 480 480 } -
trunk/src/org/openstreetmap/josm/data/SelectionChangedListener.java
r655 r1169 10 10 * a selection of any data member changes, the dataSet gets informed about this 11 11 * and fires a selectionChanged event. 12 * 12 * 13 13 * Note that these events are not fired immediately but are inserted in the 14 14 * Swing event queue and packed together. So only one selection changed event 15 15 * is issued within a one message dispatch routine. 16 * 16 * 17 17 * @author imi 18 18 */ 19 19 public interface SelectionChangedListener { 20 20 21 22 23 24 25 21 /** 22 * Informs the listener that the selection in the dataset has changed. 23 * @param newSelection The new selection. 24 */ 25 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection); 26 26 } -
trunk/src/org/openstreetmap/josm/data/ServerSidePreferences.java
r733 r1169 32 32 * This class tweak the Preferences class to provide server side preference settings, as example 33 33 * used in the applet version. 34 * 34 * 35 35 * @author Imi 36 36 */ 37 37 public class ServerSidePreferences extends Preferences { 38 38 39 39 private final Connection connection; 40 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 41 private class Connection extends OsmConnection { 42 URL serverUrl; 43 public Connection(URL serverUrl) { 44 this.serverUrl = serverUrl; 45 } 46 public String download() { 47 try { 48 System.out.println("reading preferences from "+serverUrl); 49 URLConnection con = serverUrl.openConnection(); 50 if (con instanceof HttpURLConnection) addAuth((HttpURLConnection) con); 51 con.connect(); 52 BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream())); 53 StringBuilder b = new StringBuilder(); 54 for (String line = reader.readLine(); line != null; line = reader.readLine()) { 55 b.append(line); 56 b.append("\n"); 57 } 58 if (con instanceof HttpURLConnection) ((HttpURLConnection) con).disconnect(); 59 return b.toString(); 60 } catch (IOException e) { 61 e.printStackTrace(); 62 } 63 return null; 64 } 65 public void upload(String s) { 66 try { 67 URL u = new URL(getPreferencesDir()); 68 System.out.println("uploading preferences to "+u); 69 HttpURLConnection con = (HttpURLConnection)u.openConnection(); 70 con.addRequestProperty("Authorization", "Basic "+Base64.encode(get("osm-server.username")+":"+get("osm-server.password"))); 71 con.setRequestMethod("POST"); 72 con.setDoOutput(true); 73 con.connect(); 74 PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutputStream())); 75 out.println(s); 76 out.close(); 77 con.getInputStream().close(); 78 con.disconnect(); 79 JOptionPane.showMessageDialog(Main.parent, tr("Preferences stored on {0}", u.getHost())); 80 } catch (Exception e) { 81 e.printStackTrace(); 82 JOptionPane.showMessageDialog(Main.parent, tr("Could not upload preferences. Reason: {0}", e.getMessage())); 83 } 84 } 85 } 86 86 87 88 89 90 91 92 93 94 95 96 87 public ServerSidePreferences(URL serverUrl) { 88 Connection connection = null; 89 try { 90 connection = new Connection(new URL(serverUrl+"user/preferences")); 91 } catch (MalformedURLException e) { 92 e.printStackTrace(); 93 JOptionPane.showMessageDialog(Main.parent, tr("Could not load preferences from server.")); 94 } 95 this.connection = connection; 96 } 97 97 98 99 100 98 @Override public String getPreferencesDir() { 99 return connection.serverUrl.toString(); 100 } 101 101 102 103 104 105 106 102 /** 103 * Do nothing on load. Preferences are loaded with download(). 104 */ 105 @Override public void load() { 106 } 107 107 108 109 110 111 112 108 /** 109 * Do nothing on save. Preferences are uploaded using upload(). 110 */ 111 @Override public void save() { 112 } 113 113 114 115 116 117 114 public static class Prop { 115 public String key; 116 public String value; 117 } 118 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 119 public void download(String userName, String password) { 120 resetToDefault(); 121 if (!properties.containsKey("osm-server.username") && userName != null) 122 properties.put("osm-server.username", userName); 123 if (!properties.containsKey("osm-server.password") && password != null) 124 properties.put("osm-server.password", password); 125 String cont = connection.download(); 126 if (cont == null) return; 127 Reader in = new StringReader(cont); 128 try { 129 XmlObjectParser.Uniform<Prop> parser = new XmlObjectParser.Uniform<Prop>(in, "tag", Prop.class); 130 for (Prop p : parser) 131 properties.put(p.key, p.value); 132 } catch (RuntimeException e) { 133 e.printStackTrace(); 134 } 135 } 136 136 137 138 139 140 * 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 137 /** 138 * Use this instead of save() for the ServerSidePreferences, since uploads 139 * are costly while save is called often. 140 * 141 * This is triggered by an explicit menu option. 142 */ 143 public void upload() { 144 StringBuilder b = new StringBuilder("<preferences>\n"); 145 for (Entry<String, String> p : properties.entrySet()) { 146 if (p.getKey().equals("osm-server.password")) 147 continue; // do not upload password. It would get stored in plain! 148 b.append("<tag key='"); 149 b.append(XmlWriter.encode(p.getKey())); 150 b.append("' value='"); 151 b.append(XmlWriter.encode(p.getValue())); 152 b.append("' />\n"); 153 } 154 b.append("</preferences>"); 155 connection.upload(b.toString()); 156 } 157 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 158 @Override public Collection<Bookmark> loadBookmarks() { 159 try { 160 Collection<Bookmark> bookmarks; 161 BufferedReader in = new BufferedReader(new InputStreamReader(new URL("http://"+connection.serverUrl.getHost()+"/josm/bookmarks").openStream())); 162 bookmarks = new LinkedList<Bookmark>(); 163 for (String line = in.readLine(); line != null; line = in.readLine()) { 164 StringTokenizer st = new StringTokenizer(line, ","); 165 if (st.countTokens() < 5) 166 continue; 167 Bookmark b = new Bookmark(); 168 b.name = st.nextToken(); 169 try { 170 for (int i = 0; i < b.latlon.length; ++i) 171 b.latlon[i] = Double.parseDouble(st.nextToken()); 172 bookmarks.add(b); 173 } catch (NumberFormatException x) { 174 // line not parsed 175 } 176 } 177 in.close(); 178 return bookmarks; 179 } catch (MalformedURLException e) { 180 e.printStackTrace(); 181 } catch (IllegalArgumentException e) { 182 e.printStackTrace(); 183 } catch (IOException e) { 184 e.printStackTrace(); 185 } 186 return Collections.emptyList(); 187 } 188 188 189 190 189 @Override public void saveBookmarks(Collection<Bookmark> bookmarks) { 190 } 191 191 } -
trunk/src/org/openstreetmap/josm/data/UndoRedoHandler.java
r661 r1169 15 15 public class UndoRedoHandler implements LayerChangeListener { 16 16 17 18 19 20 21 22 23 24 17 /** 18 * All commands that were made on the dataset. Don't write from outside! 19 */ 20 public final LinkedList<Command> commands = new LinkedList<Command>(); 21 /** 22 * The stack for redoing commands 23 */ 24 private final Stack<Command> redoCommands = new Stack<Command>(); 25 25 26 26 public final LinkedList<CommandQueueListener> listenerCommands = new LinkedList<CommandQueueListener>(); 27 27 28 28 29 30 31 29 public UndoRedoHandler() { 30 Layer.listeners.add(this); 31 } 32 32 33 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 34 /** 35 * Execute the command and add it to the intern command queue. 36 */ 37 public void add(final Command c) { 38 c.executeCommand(); 39 commands.add(c); 40 redoCommands.clear(); 41 if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) { 42 OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer(); 43 data.setModified(true); 44 data.fireDataChange(); 45 } 46 fireCommandsChanged(); 47 } 48 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 } 63 64 65 49 /** 50 * Undoes the last added command. 51 */ 52 public void undo() { 53 if (commands.isEmpty()) 54 return; 55 final Command c = commands.removeLast(); 56 c.undoCommand(); 57 redoCommands.push(c); 58 if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) { 59 OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer(); 60 data.setModified(data.uploadedModified || !commands.isEmpty()); 61 data.fireDataChange(); 62 } 63 fireCommandsChanged(); 64 Main.ds.setSelected(); 65 } 66 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 67 /** 68 * Redoes the last undoed command. 69 * TODO: This has to be moved to a central place in order to support multiple layers. 70 */ 71 public void redo() { 72 if (redoCommands.isEmpty()) 73 return; 74 final Command c = redoCommands.pop(); 75 c.executeCommand(); 76 commands.add(c); 77 if (Main.map != null && Main.map.mapView.getActiveLayer() instanceof OsmDataLayer) { 78 OsmDataLayer data = (OsmDataLayer)Main.map.mapView.getActiveLayer(); 79 data.setModified(true); 80 data.fireDataChange(); 81 } 82 fireCommandsChanged(); 83 } 84 84 85 86 87 88 85 public void fireCommandsChanged() { 86 for (final CommandQueueListener l : listenerCommands) 87 l.commandChanged(commands.size(), redoCommands.size()); 88 } 89 89 90 91 92 93 94 90 public void clean() { 91 redoCommands.clear(); 92 commands.clear(); 93 fireCommandsChanged(); 94 } 95 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 96 public void layerRemoved(Layer oldLayer) { 97 boolean changed = false; 98 for (Iterator<Command> it = commands.iterator(); it.hasNext();) { 99 if (it.next().invalidBecauselayerRemoved(oldLayer)) { 100 it.remove(); 101 changed = true; 102 } 103 } 104 for (Iterator<Command> it = redoCommands.iterator(); it.hasNext();) { 105 if (it.next().invalidBecauselayerRemoved(oldLayer)) { 106 it.remove(); 107 changed = true; 108 } 109 } 110 if (changed) 111 fireCommandsChanged(); 112 } 113 113 114 115 114 public void layerAdded(Layer newLayer) {} 115 public void activeLayerChange(Layer oldLayer, Layer newLayer) {} 116 116 } -
trunk/src/org/openstreetmap/josm/data/conflict/ConflictItem.java
r627 r1169 12 12 public abstract class ConflictItem { 13 13 14 15 14 public String my, their; 15 public Resolution resolution = null; 16 16 17 18 19 20 17 public final void initialize(Map<OsmPrimitive,OsmPrimitive> conflicts) { 18 my = collectStr(conflicts.keySet()); 19 their = collectStr(conflicts.values()); 20 } 21 21 22 23 24 25 22 abstract public boolean hasConflict(OsmPrimitive key, OsmPrimitive value); 23 abstract protected String str(OsmPrimitive osm); 24 abstract public String key(); 25 abstract public void apply(OsmPrimitive target, OsmPrimitive other); 26 26 27 28 29 30 31 32 33 34 35 36 37 38 39 27 protected final String collectStr(Collection<OsmPrimitive> c) { 28 String value = null; 29 for (OsmPrimitive osm : c) { 30 String v = str(osm); 31 if (value == null) 32 value = v; 33 else if (!value.equals(v)) { 34 value = "<html><i><"+tr("different")+"></i></html>"; 35 break; 36 } 37 } 38 return value == null ? "" : value; 39 } 40 40 } -
trunk/src/org/openstreetmap/josm/data/conflict/DeleteConflict.java
r627 r1169 8 8 public class DeleteConflict extends ConflictItem { 9 9 10 11 12 10 @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) { 11 return key.deleted != value.deleted; 12 } 13 13 14 15 16 14 @Override public String key() { 15 return "deleted|"+tr("deleted"); 16 } 17 17 18 19 20 18 @Override protected String str(OsmPrimitive osm) { 19 return osm.deleted ? tr("true") : tr("false"); 20 } 21 21 22 23 24 22 @Override public void apply(OsmPrimitive target, OsmPrimitive other) { 23 target.deleted = other.deleted; 24 } 25 25 } -
trunk/src/org/openstreetmap/josm/data/conflict/PositionConflict.java
r627 r1169 8 8 9 9 public class PositionConflict extends ConflictItem { 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 10 11 @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) { 12 return key instanceof Node && !((Node)key).coor.equals(((Node)value).coor); 13 } 14 15 @Override protected String str(OsmPrimitive osm) { 16 return osm instanceof Node ? ((Node)osm).coor.lat()+", "+((Node)osm).coor.lon() : null; 17 } 18 19 @Override public String key() { 20 return "node|"+tr("position"); 21 } 22 23 @Override public void apply(OsmPrimitive target, OsmPrimitive other) { 24 if (target instanceof Node) { 25 ((Node)target).coor = ((Node)other).coor; 26 ((Node)target).eastNorth = ((Node)other).eastNorth; 27 } 28 28 } 29 29 } -
trunk/src/org/openstreetmap/josm/data/conflict/PropertyConflict.java
r627 r1169 5 5 6 6 public class PropertyConflict extends ConflictItem { 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 7 private String key; 8 9 public PropertyConflict(String key) { 10 this.key = key; 11 } 12 13 @Override public boolean hasConflict(OsmPrimitive key, OsmPrimitive value) { 14 String k = key.get(this.key); 15 String v = value.get(this.key); 16 return k == null ? v!=null : !k.equals(v); 17 } 18 19 @Override protected String str(OsmPrimitive osm) { 20 String v = osm.get(key); 21 return v == null ? "" : v; 22 } 23 24 @Override public String key() { 25 return "key|"+key; 26 } 27 28 @Override public void apply(OsmPrimitive target, OsmPrimitive other) { 29 target.put(key, other.get(key)); 30 30 } 31 31 } -
trunk/src/org/openstreetmap/josm/data/coor/Coordinate.java
r1094 r1169 7 7 /** 8 8 * Base class of points of both coordinate systems. 9 * 10 * The variables are default package protected to allow routines in the 9 * 10 * The variables are default package protected to allow routines in the 11 11 * data package to access them directly. 12 * 13 * As the class itself is package protected too, it is not visible 14 * outside of the data package. Routines there should only use LatLon or 12 * 13 * As the class itself is package protected too, it is not visible 14 * outside of the data package. Routines there should only use LatLon or 15 15 * EastNorth. 16 16 * 17 17 * @author imi 18 */ 18 */ 19 19 abstract class Coordinate extends Point2D implements Serializable { 20 20 21 21 protected double x; 22 22 protected double y; 23 24 25 26 * 27 28 29 30 23 24 /** 25 * Construct the point with latitude / longitude values. 26 * 27 * @param x X coordinate of the point. 28 * @param y Y coordinate of the point. 29 */ 30 Coordinate(double x, double y) { 31 31 this.x = x; this.y = y; 32 33 32 } 33 34 34 public double getX() { 35 35 return x; 36 36 } 37 37 38 38 public double getY() { 39 return y; 39 return y; 40 40 } 41 41 42 42 public void setLocation (double x, double y) { 43 this.x = x; 43 this.x = x; 44 44 this.y = y; 45 45 } -
trunk/src/org/openstreetmap/josm/data/coor/EastNorth.java
r1076 r1169 4 4 /** 5 5 * Northing, Easting of the projected coordinates. 6 * 6 * 7 7 * This class is immutable. 8 * 8 * 9 9 * @author Imi 10 10 */ 11 11 public class EastNorth extends Coordinate { 12 12 13 public EastNorth(double east, double north) { 14 super(east,north); 15 } 16 17 public double east() { 18 return x; 19 } 13 public EastNorth(double east, double north) { 14 super(east,north); 15 } 20 16 21 public double north() {22 return y;23 17 public double east() { 18 return x; 19 } 24 20 25 public EastNorth add(double dx, double dy) { 26 return new EastNorth(x+dx, y+dy); 27 } 28 29 public EastNorth interpolate(EastNorth en2, double proportion) { 30 return new EastNorth(this.x + proportion * (en2.x - this.x), 21 public double north() { 22 return y; 23 } 24 25 public EastNorth add(double dx, double dy) { 26 return new EastNorth(x+dx, y+dy); 27 } 28 29 public EastNorth interpolate(EastNorth en2, double proportion) { 30 return new EastNorth(this.x + proportion * (en2.x - this.x), 31 31 this.y + proportion * (en2.y - this.y)); 32 33 32 } 33 34 34 /** 35 * Returns the heading, in radians, that you have to use to get from 35 * Returns the heading, in radians, that you have to use to get from 36 36 * this EastNorth to another. Heading is mapped into [0, 2pi) 37 * 37 * 38 38 * @param other the "destination" position 39 * @return heading 39 * @return heading 40 40 */ 41 41 public double heading(EastNorth other) { 42 42 double hd = Math.atan2(other.east() - east(), other.north() - north()); 43 43 if(hd < 0) hd = 2 * Math.PI + hd; 44 return hd; 44 return hd; 45 45 } 46 46 47 47 public EastNorth sub(EastNorth en) { 48 48 return new EastNorth(en.east() - east(), en.north() - north()); 49 49 } 50 50 51 51 /** 52 52 * Returns an EastNorth representing the this EastNorth rotatedaround … … 54 54 * @param pivot the center of the rotation 55 55 * @param angle the angle of the rotation 56 * @return EastNorth rotated object 56 * @return EastNorth rotated object 57 57 */ 58 58 public EastNorth rotate(EastNorth pivot, double angle) { … … 65 65 return new EastNorth(nx, ny); 66 66 } 67 68 69 70 67 68 @Override public String toString() { 69 return "EastNorth[e="+x+", n="+y+"]"; 70 } 71 71 72 72 /** -
trunk/src/org/openstreetmap/josm/data/coor/LatLon.java
r1108 r1169 13 13 /** 14 14 * LatLon are unprojected latitude / longitude coordinates. 15 * 15 * 16 16 * This class is immutable. 17 * 17 * 18 18 * @author Imi 19 19 */ … … 23 23 private static DecimalFormat cDmsSecondFormatter = new DecimalFormat("00.0"); 24 24 private static DecimalFormat cDdFormatter = new DecimalFormat("###0.0000"); 25 26 /** 27 * Possible ways to display coordinates 28 */ 29 public enum CoordinateFormat { 30 DECIMAL_DEGREES {public String toString() {return tr("Decimal Degrees");}}, 31 DEGREES_MINUTES_SECONDS {public String toString() {return tr("Degrees Minutes Seconds");}}; 32 } 33 34 public static String dms(double pCoordinate) { 25 26 /** 27 * Possible ways to display coordinates 28 */ 29 public enum CoordinateFormat { 30 DECIMAL_DEGREES {public String toString() {return tr("Decimal Degrees");}}, 31 DEGREES_MINUTES_SECONDS {public String toString() {return tr("Degrees Minutes Seconds");}}; 32 } 33 34 public static String dms(double pCoordinate) { 35 35 36 36 double tAbsCoord = Math.abs(pCoordinate); … … 40 40 double tSeconds = (tTmpMinutes - tMinutes) * 60; 41 41 42 return tDegree + "\u00B0" + cDmsMinuteFormatter.format(tMinutes) + "\'" 42 return tDegree + "\u00B0" + cDmsMinuteFormatter.format(tMinutes) + "\'" 43 43 + cDmsSecondFormatter.format(tSeconds) + "\""; 44 } 44 } 45 45 46 46 public LatLon(double lat, double lon) { … … 51 51 return y; 52 52 } 53 53 54 54 public String latToString(CoordinateFormat d) { 55 55 switch(d) { … … 59 59 } 60 60 } 61 61 62 62 public double lon() { 63 63 return x; 64 64 } 65 65 66 66 public String lonToString(CoordinateFormat d) { 67 67 switch(d) { … … 71 71 } 72 72 } 73 74 73 74 75 75 76 76 /** … … 86 86 /** 87 87 * @return <code>true</code>, if the coordinate is outside the world, compared 88 89 90 91 return lat() < -Projection.MAX_LAT || lat() > Projection.MAX_LAT || 92 93 88 * by using lat/lon. 89 */ 90 public boolean isOutSideWorld() { 91 return lat() < -Projection.MAX_LAT || lat() > Projection.MAX_LAT || 92 lon() < -Projection.MAX_LON || lon() > Projection.MAX_LON; 93 } 94 94 95 /** 96 * @return <code>true</code> if this is within the given bounding box. 97 */ 98 public boolean isWithin(Bounds b) { 99 return lat() >= b.min.lat() && lat() <= b.max.lat() && lon() > b.min.lon() && lon() < b.max.lon(); 100 } 101 102 /** 103 * Computes the distance between this lat/lon and another point on the earth. 104 * Uses spherical law of cosines formula, not Haversine. 105 * @param other the other point. 106 * @return distance in metres. 107 */ 108 public double greatCircleDistance(LatLon other) { 109 return (Math.acos( 110 Math.sin(Math.toRadians(lat())) * Math.sin(Math.toRadians(other.lat())) + 111 Math.cos(Math.toRadians(lat()))*Math.cos(Math.toRadians(other.lat())) * 112 Math.cos(Math.toRadians(other.lon()-lon()))) * 6378135); 113 } 114 115 /** 116 * Returns the heading, in radians, that you have to use to get from 117 * this lat/lon to another. 118 * 119 * @param other the "destination" position 120 * @return heading 121 */ 122 public double heading(LatLon other) { 123 double rv; 124 if (other.lat() == lat()) { 125 rv = (other.lon()>lon() ? Math.PI / 2 : Math.PI * 3 / 2); 126 } else { 127 rv = Math.atan((other.lon()-lon())/(other.lat()-lat())); 128 if (rv < 0) rv += Math.PI; 129 if (other.lon() < lon()) rv += Math.PI; 130 } 131 return rv; 132 } 95 /** 96 * @return <code>true</code> if this is within the given bounding box. 97 */ 98 public boolean isWithin(Bounds b) { 99 return lat() >= b.min.lat() && lat() <= b.max.lat() && lon() > b.min.lon() && lon() < b.max.lon(); 100 } 133 101 134 /** 135 * Returns this lat/lon pair in human-readable format. 136 * 137 * @return String in the format "lat=1.23456°, lon=2.34567°" 138 */ 139 public String toDisplayString() { 140 NumberFormat nf = NumberFormat.getInstance(); 141 nf.setMaximumFractionDigits(5); 142 return "lat=" + nf.format(lat()) + "°, lon=" + nf.format(lon()) + "°"; 143 } 144 145 @Override public String toString() { 146 return "LatLon[lat="+lat()+",lon="+lon()+"]"; 102 /** 103 * Computes the distance between this lat/lon and another point on the earth. 104 * Uses spherical law of cosines formula, not Haversine. 105 * @param other the other point. 106 * @return distance in metres. 107 */ 108 public double greatCircleDistance(LatLon other) { 109 return (Math.acos( 110 Math.sin(Math.toRadians(lat())) * Math.sin(Math.toRadians(other.lat())) + 111 Math.cos(Math.toRadians(lat()))*Math.cos(Math.toRadians(other.lat())) * 112 Math.cos(Math.toRadians(other.lon()-lon()))) * 6378135); 113 } 114 115 /** 116 * Returns the heading, in radians, that you have to use to get from 117 * this lat/lon to another. 118 * 119 * @param other the "destination" position 120 * @return heading 121 */ 122 public double heading(LatLon other) { 123 double rv; 124 if (other.lat() == lat()) { 125 rv = (other.lon()>lon() ? Math.PI / 2 : Math.PI * 3 / 2); 126 } else { 127 rv = Math.atan((other.lon()-lon())/(other.lat()-lat())); 128 if (rv < 0) rv += Math.PI; 129 if (other.lon() < lon()) rv += Math.PI; 130 } 131 return rv; 132 } 133 134 /** 135 * Returns this lat/lon pair in human-readable format. 136 * 137 * @return String in the format "lat=1.23456°, lon=2.34567°" 138 */ 139 public String toDisplayString() { 140 NumberFormat nf = NumberFormat.getInstance(); 141 nf.setMaximumFractionDigits(5); 142 return "lat=" + nf.format(lat()) + "°, lon=" + nf.format(lon()) + "°"; 143 } 144 145 @Override public String toString() { 146 return "LatLon[lat="+lat()+",lon="+lon()+"]"; 147 147 } 148 148 } -
trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
r655 r1169 15 15 * It uses GPX v1.1, see {@link <a href="http://www.topografix.com/GPX/1/1/">the spec</a>} 16 16 * for details. 17 * 17 * 18 18 * @author Raphael Mack <ramack@raphael-mack.de> 19 19 */ 20 20 public class GpxData extends WithAttributes { 21 22 21 public File storageFile; 22 public boolean fromServer; 23 23 24 25 26 24 public Collection<GpxTrack> tracks = new LinkedList<GpxTrack>(); 25 public Collection<GpxRoute> routes = new LinkedList<GpxRoute>(); 26 public Collection<WayPoint> waypoints = new LinkedList<WayPoint>(); 27 27 28 28 public Bounds bounds; 29 29 30 31 32 33 34 30 public void mergeFrom(GpxData other) { 31 if (storageFile == null && other.storageFile != null) { 32 storageFile = other.storageFile; 33 } 34 fromServer = fromServer && other.fromServer; 35 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 36 for (Map.Entry<String, Object> ent : other.attr.entrySet()) { 37 // TODO: Detect conflicts. 38 String k = ent.getKey(); 39 if (k.equals("link") && attr.containsKey("link")) { 40 ((Collection<GpxLink>) attr.get("link")).addAll( 41 (Collection<GpxLink>) ent.getValue()); 42 } else { 43 attr.put(k, ent.getValue()); 44 } 45 } 46 tracks.addAll(other.tracks); 47 routes.addAll(other.routes); 48 waypoints.addAll(other.waypoints); 49 } 50 50 51 52 53 54 55 56 57 58 59 51 public boolean hasTrackPoints() { 52 for (GpxTrack trk : tracks) { 53 for (Collection<WayPoint> trkseg : trk.trackSegs) { 54 if (!trkseg.isEmpty()) 55 return true; 56 } 57 } 58 return false; 59 } 60 60 61 62 63 64 65 66 67 61 public boolean hasRoutePoints() { 62 for (GpxRoute rte : routes) { 63 if (!rte.routePoints.isEmpty()) 64 return true; 65 } 66 return false; 67 } 68 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 69 // FIXME might perhaps use visitor pattern? 70 public void recalculateBounds() { 71 bounds = null; 72 for (WayPoint wpt : waypoints) { 73 if (bounds == null) { 74 bounds = new Bounds(wpt.latlon, wpt.latlon); 75 } else { 76 bounds.extend(wpt.latlon); 77 } 78 } 79 for (GpxRoute rte : routes) { 80 for (WayPoint wpt : rte.routePoints) { 81 if (bounds == null) { 82 bounds = new Bounds(wpt.latlon, wpt.latlon); 83 } else { 84 bounds.extend(wpt.latlon); 85 } 86 } 87 } 88 for (GpxTrack trk : tracks) { 89 for (Collection<WayPoint> trkseg : trk.trackSegs) { 90 for (WayPoint wpt : trkseg) { 91 if (bounds == null) { 92 bounds = new Bounds(wpt.latlon, wpt.latlon); 93 } else { 94 bounds.extend(wpt.latlon); 95 } 96 } 97 } 98 } 99 if (bounds == null) { 100 bounds = new Bounds(); 101 } 102 } 103 104 104 /** 105 105 * calculates the sum of the lengths of all track segments … … 108 108 double result = 0.0; // in meters 109 109 WayPoint last = null; 110 110 111 111 for (GpxTrack trk : tracks) { 112 112 for (Collection<WayPoint> trkseg : trk.trackSegs) { … … 129 129 double lat1, lon1, lat2, lon2; 130 130 double dlon, dlat; 131 131 132 132 lat1 = p1.lat() * Math.PI / 180.0; 133 133 lon1 = p1.lon() * Math.PI / 180.0; -
trunk/src/org/openstreetmap/josm/data/gpx/GpxLink.java
r627 r1169 5 5 6 6 public class GpxLink { 7 8 9 7 public String uri; 8 public String text; 9 public String type; 10 10 11 12 13 11 public GpxLink(String uri) { 12 this.uri = uri; 13 } 14 14 } -
trunk/src/org/openstreetmap/josm/data/gpx/GpxRoute.java
r627 r1169 8 8 9 9 public class GpxRoute extends WithAttributes { 10 10 public Collection<WayPoint> routePoints = new LinkedList<WayPoint>(); 11 11 } -
trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
r627 r1169 8 8 9 9 public class GpxTrack extends WithAttributes { 10 11 10 public Collection<Collection<WayPoint>> trackSegs 11 = new LinkedList<Collection<WayPoint>>(); 12 12 } -
trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
r1167 r1169 15 15 public class WayPoint extends WithAttributes implements Comparable<WayPoint> 16 16 { 17 18 19 20 21 22 17 public final LatLon latlon; 18 public final EastNorth eastNorth; 19 public double time; 20 public Color speedLineColor; 21 public boolean drawLine; 22 public int dir; 23 23 24 25 26 27 24 public WayPoint(LatLon ll) { 25 latlon = ll; 26 eastNorth = Main.proj.latlon2eastNorth(ll); 27 } 28 28 29 @Override 30 public String toString() { 31 return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + latlon.toString() + ", " + attr + ")"; 32 } 33 34 /** 35 * Convert the time stamp of the waypoint into seconds from the epoch 36 */ 37 public final static SimpleDateFormat GPXTIMEFMT = 38 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); // ignore timezone 39 40 public void setTime() { 41 if (! attr.containsKey("time")) { 42 return; 43 } 44 Date d = GPXTIMEFMT.parse(attr.get("time").toString(), new ParsePosition(0)); 45 if (d != null /* parsing ok */) { 46 time = d.getTime() / 1000.0; /* ms => seconds */ 47 } 48 } 29 @Override 30 public String toString() { 31 return "WayPoint (" + (attr.containsKey("name") ? attr.get("name") + ", " :"") + latlon.toString() + ", " + attr + ")"; 32 } 49 33 50 /** 51 * Convert a time stamp of the waypoint from the <cmt> or <desc> field 52 * into seconds from the epoch. Handles the date format as it is used by 53 * Garmin handhelds. Does not overwrite an existing timestamp (!= 0.0). 54 * A value of <time> fields overwrites values set with by method. 55 * Does nothing if specified key does not exist or text cannot be parsed. 56 * 57 * @param key The key that contains the text to convert. 58 */ 59 public void setGarminCommentTime(String key) { 60 // do not overwrite time if already set 61 if (time != 0.0) { 62 return; 63 } 64 if (! attr.containsKey(key)) { 65 return; 66 } 67 // example date format "18-AUG-08 13:33:03" 68 SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss"); // Garmin wpts have no timezone 69 Date d = f.parse(attr.get(key).toString(), new ParsePosition(0)); 70 if (d != null /* parsing OK */) { 71 time = d.getTime() / 1000.0; /* ms => seconds */ 72 } 73 } 34 /** 35 * Convert the time stamp of the waypoint into seconds from the epoch 36 */ 37 public final static SimpleDateFormat GPXTIMEFMT = 38 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); // ignore timezone 74 39 75 public int compareTo(WayPoint w) 76 { 77 return Double.compare(time, w.time); 78 } 40 public void setTime() { 41 if (! attr.containsKey("time")) { 42 return; 43 } 44 Date d = GPXTIMEFMT.parse(attr.get("time").toString(), new ParsePosition(0)); 45 if (d != null /* parsing ok */) { 46 time = d.getTime() / 1000.0; /* ms => seconds */ 47 } 48 } 49 50 /** 51 * Convert a time stamp of the waypoint from the <cmt> or <desc> field 52 * into seconds from the epoch. Handles the date format as it is used by 53 * Garmin handhelds. Does not overwrite an existing timestamp (!= 0.0). 54 * A value of <time> fields overwrites values set with by method. 55 * Does nothing if specified key does not exist or text cannot be parsed. 56 * 57 * @param key The key that contains the text to convert. 58 */ 59 public void setGarminCommentTime(String key) { 60 // do not overwrite time if already set 61 if (time != 0.0) { 62 return; 63 } 64 if (! attr.containsKey(key)) { 65 return; 66 } 67 // example date format "18-AUG-08 13:33:03" 68 SimpleDateFormat f = new SimpleDateFormat("dd-MMM-yy HH:mm:ss"); // Garmin wpts have no timezone 69 Date d = f.parse(attr.get(key).toString(), new ParsePosition(0)); 70 if (d != null /* parsing OK */) { 71 time = d.getTime() / 1000.0; /* ms => seconds */ 72 } 73 } 74 75 public int compareTo(WayPoint w) 76 { 77 return Double.compare(time, w.time); 78 } 79 79 } -
trunk/src/org/openstreetmap/josm/data/gpx/WithAttributes.java
r627 r1169 1 // License: GPL. 1 // License: GPL. 2 2 package org.openstreetmap.josm.data.gpx; 3 3 … … 9 9 * The "attr" hash is used to store the XML payload 10 10 * (not only XML attributes!) 11 * 11 * 12 12 * @author Frederik Ramm <frederik@remote.org> 13 13 * 14 14 */ 15 15 public class WithAttributes { 16 17 18 19 20 21 22 16 17 public Map<String, Object> attr = new HashMap<String, Object>(0); 18 19 public String getString(String key) { 20 Object value = attr.get(key); 21 return (value instanceof String) ? (String)value : null; 22 } 23 23 } -
trunk/src/org/openstreetmap/josm/data/osm/Changeset.java
r655 r1169 20 20 */ 21 21 public final class Changeset /*extends OsmPrimitive*/ implements OsmWriterInterface { 22 23 24 25 22 /** 23 * The key/value list for this primitive. 24 */ 25 public Map<String, String> keys; 26 26 27 28 29 /** 30 31 32 33 34 35 36 37 * read from the server and delivered back to the server unmodified. 38 39 40 41 42 43 44 45 27 public long id = 0; 28 29 /** 30 * User that created this changeset, as specified by the server. 31 * Never changed by JOSM. 32 */ 33 public User user = null; 34 35 /** 36 * Time of last modification to this object. This is not set by JOSM but 37 * read from the server and delivered back to the server unmodified. 38 */ 39 public String end_timestamp = null; 40 41 /** 42 * Time of first modification to this object. This is not set by JOSM but 43 * read from the server and delivered back to the server unmodified. 44 */ 45 public String start_timestamp = null; 46 46 47 47 private void addTags(PrintWriter out) { … … 53 53 } 54 54 55 56 55 public final void header(PrintWriter out) { 56 out.print("<osm version='"); 57 57 out.print(Main.pref.get("osm-server.version", "0.6")); 58 58 out.println("' generator='JOSM'>"); 59 } 60 public final void write(PrintWriter out) { 61 out.print(" <changeset"); 62 if (id != 0) 63 out.print(" id="+id); 64 if (this.user != null) { 65 out.print(" user='"+XmlWriter.encode(this.user.name)+"'"); 66 } 67 out.println(">\n"); 68 addTags( out ); 69 out.println(" </changeset>"); 70 } 71 public final void footer(PrintWriter out) { 72 out.println("</osm>"); 73 } 74 75 /****************************************************** 76 * This tag stuff is copied from OsmPrimitive. Perhaps a changeset 77 * really is a primitive, but it's not right now. Perhaps it should 78 * be... 79 ******************************************************/ 80 81 /** 82 * Set the given value to the given key 83 * @param key The key, for which the value is to be set. 84 * @param value The value for the key. 85 */ 86 public final void put(String key, String value) { 87 if (value == null) 88 remove(key); 89 else { 90 if (keys == null) 91 keys = new HashMap<String, String>(); 92 keys.put(key, value); 93 } 94 } 95 /** 96 * Remove the given key from the list. 97 */ 98 public final void remove(String key) { 99 if (keys != null) { 100 keys.remove(key); 101 if (keys.isEmpty()) 102 keys = null; 103 } 104 } 59 } 60 public final void write(PrintWriter out) { 61 out.print(" <changeset"); 62 if (id != 0) 63 out.print(" id="+id); 64 if (this.user != null) { 65 out.print(" user='"+XmlWriter.encode(this.user.name)+"'"); 66 } 67 out.println(">\n"); 68 addTags( out ); 69 out.println(" </changeset>"); 70 } 71 public final void footer(PrintWriter out) { 72 out.println("</osm>"); 73 } 105 74 106 public final String get(String key) { 107 return keys == null ? null : keys.get(key); 108 } 75 /****************************************************** 76 * This tag stuff is copied from OsmPrimitive. Perhaps a changeset 77 * really is a primitive, but it's not right now. Perhaps it should 78 * be... 79 ******************************************************/ 109 80 110 public final Collection<Entry<String, String>> entrySet() { 111 if (keys == null) 112 return Collections.emptyList(); 113 return keys.entrySet(); 114 } 81 /** 82 * Set the given value to the given key 83 * @param key The key, for which the value is to be set. 84 * @param value The value for the key. 85 */ 86 public final void put(String key, String value) { 87 if (value == null) 88 remove(key); 89 else { 90 if (keys == null) 91 keys = new HashMap<String, String>(); 92 keys.put(key, value); 93 } 94 } 95 /** 96 * Remove the given key from the list. 97 */ 98 public final void remove(String key) { 99 if (keys != null) { 100 keys.remove(key); 101 if (keys.isEmpty()) 102 keys = null; 103 } 104 } 115 105 116 public final Collection<String> keySet() { 117 if (keys == null) 118 return Collections.emptyList(); 119 return keys.keySet(); 120 } 106 public final String get(String key) { 107 return keys == null ? null : keys.get(key); 108 } 109 110 public final Collection<Entry<String, String>> entrySet() { 111 if (keys == null) 112 return Collections.emptyList(); 113 return keys.entrySet(); 114 } 115 116 public final Collection<String> keySet() { 117 if (keys == null) 118 return Collections.emptyList(); 119 return keys.keySet(); 120 } 121 121 } -
trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
r1004 r1169 17 17 * DataSet is the data behind the application. It can consists of only a few points up to the whole 18 18 * osm database. DataSet's can be merged together, saved, (up/down/disk)loaded etc. 19 * 19 * 20 20 * Note that DataSet is not an osm-primitive and so has no key association but a few members to 21 21 * store some information. 22 * 22 * 23 23 * @author imi 24 24 */ … … 33 33 /** 34 34 * All ways (Streets etc.) in the DataSet. 35 * 35 * 36 36 * The way nodes are stored only in the way list. 37 37 */ … … 235 235 236 236 // Provide well-defined sorting for collections of OsmPrimitives. 237 // FIXME: probably not a good place to put this code. 237 // FIXME: probably not a good place to put this code. 238 238 public static OsmPrimitive[] sort(Collection<? extends OsmPrimitive> list) { 239 239 OsmPrimitive[] selArr = new OsmPrimitive[list.size()]; -
trunk/src/org/openstreetmap/josm/data/osm/DataSource.java
r627 r1169 5 5 6 6 public class DataSource implements Cloneable { 7 8 9 10 11 12 7 public final Bounds bounds; 8 public final String origin; 9 10 public DataSource(Bounds bounds, String origin) { 11 this.bounds = bounds; 12 this.origin = origin; 13 13 } 14 14 15 16 15 @Override protected Object clone() throws CloneNotSupportedException { 16 return new DataSource(bounds, origin); 17 17 } 18 18 } -
trunk/src/org/openstreetmap/josm/data/osm/DateFormatter.java
r627 r1169 9 9 /** 10 10 * Outputs a date in a format suitable for an OSM XML file. 11 * 11 * 12 12 * @author Brett Henderson 13 13 */ 14 14 public class DateFormatter { 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 * 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 15 16 private GregorianCalendar calendar; 17 18 19 /** 20 * Creates a new instance. 21 */ 22 public DateFormatter() { 23 calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 24 } 25 26 27 /** 28 * Formats a date in XML format. 29 * 30 * @param date 31 * The date to be formatted. 32 * @return The string representing the date. 33 */ 34 public String format(Date date) { 35 StringBuilder result; 36 int year; 37 int month; 38 int day; 39 int hour; 40 int minute; 41 int second; 42 43 calendar.setTime(date); 44 45 result = new StringBuilder(20); 46 47 year = calendar.get(Calendar.YEAR); 48 month = calendar.get(Calendar.MONTH) + 1; 49 day = calendar.get(Calendar.DATE); 50 hour = calendar.get(Calendar.HOUR_OF_DAY); 51 minute = calendar.get(Calendar.MINUTE); 52 second = calendar.get(Calendar.SECOND); 53 54 result.append(year); 55 result.append('-'); 56 if (month < 10) { 57 result.append('0'); 58 } 59 result.append(month); 60 result.append('-'); 61 if (day < 10) { 62 result.append('0'); 63 } 64 result.append(day); 65 result.append('T'); 66 if (hour < 10) { 67 result.append('0'); 68 } 69 result.append(hour); 70 result.append(':'); 71 if (minute < 10) { 72 result.append('0'); 73 } 74 result.append(minute); 75 result.append(':'); 76 if (second < 10) { 77 result.append('0'); 78 } 79 result.append(second); 80 result.append('Z'); 81 82 return result.toString(); 83 } 84 84 } -
trunk/src/org/openstreetmap/josm/data/osm/Node.java
r1108 r1169 19 19 */ 20 20 public final class Node extends OsmPrimitive { 21 22 23 24 21 22 public LatLon coor; 23 public volatile EastNorth eastNorth; 24 25 25 private static CoordinateFormat mCord; 26 26 27 27 static { 28 28 try { … … 33 33 } 34 34 35 /** 36 * Create an incomplete Node object 37 */ 38 public Node(long id) { 39 this.id = id; 40 incomplete = true; 41 } 42 43 /** 44 * Create an identical clone of the argument (including the id) 45 */ 46 public Node(Node clone) { 47 cloneFrom(clone); 48 } 35 /** 36 * Create an incomplete Node object 37 */ 38 public Node(long id) { 39 this.id = id; 40 incomplete = true; 41 } 49 42 50 public Node(LatLon latlon) { 51 this.coor = latlon; 52 eastNorth = Main.proj.latlon2eastNorth(latlon); 53 } 43 /** 44 * Create an identical clone of the argument (including the id) 45 */ 46 public Node(Node clone) { 47 cloneFrom(clone); 48 } 54 49 55 @Override public void visit(Visitor visitor) { 56 visitor.visit(this); 57 } 58 59 @Override public void cloneFrom(OsmPrimitive osm) { 60 super.cloneFrom(osm); 61 coor = ((Node)osm).coor; 62 eastNorth = ((Node)osm).eastNorth; 63 } 50 public Node(LatLon latlon) { 51 this.coor = latlon; 52 eastNorth = Main.proj.latlon2eastNorth(latlon); 53 } 64 54 65 @Override public String toString() { 66 if (coor == null) return "{Node id="+id+"}"; 67 return "{Node id="+id+",version="+version+",lat="+coor.lat()+",lon="+coor.lon()+"}"; 68 } 55 @Override public void visit(Visitor visitor) { 56 visitor.visit(this); 57 } 69 58 70 @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 71 if (osm instanceof Node) { 59 @Override public void cloneFrom(OsmPrimitive osm) { 60 super.cloneFrom(osm); 61 coor = ((Node)osm).coor; 62 eastNorth = ((Node)osm).eastNorth; 63 } 64 65 @Override public String toString() { 66 if (coor == null) return "{Node id="+id+"}"; 67 return "{Node id="+id+",version="+version+",lat="+coor.lat()+",lon="+coor.lon()+"}"; 68 } 69 70 @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 71 if (osm instanceof Node) { 72 72 if (super.realEqual(osm, semanticOnly)) { 73 73 if ((coor == null) && ((Node)osm).coor == null) … … 80 80 } 81 81 82 83 84 82 public int compareTo(OsmPrimitive o) { 83 return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1; 84 } 85 85 86 87 88 89 90 91 92 93 94 95 96 97 86 public String getName() { 87 String name; 88 if (incomplete) { 89 name = tr("incomplete"); 90 } else { 91 name = get("name"); 92 if (name == null) 93 name = id == 0 ? "" : ""+id; 94 name += " (" + coor.latToString(mCord) + ", " + coor.lonToString(mCord) + ")"; 95 } 96 return name; 97 } 98 98 } -
trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
r795 r1169 30 30 abstract public class OsmPrimitive implements Comparable<OsmPrimitive> { 31 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 32 /** 33 * The key/value list for this primitive. 34 */ 35 public Map<String, String> keys; 36 37 /** 38 * Unique identifier in OSM. This is used to identify objects on the server. 39 * An id of 0 means an unknown id. The object has not been uploaded yet to 40 * know what id it will get. 41 * 42 * Do not write to this attribute except you know exactly what you are doing. 43 * More specific, it is not good to set this to 0 and think the object is now 44 * new to the server! To create a new object, call the default constructor of 45 * the respective class. 46 */ 47 public long id = 0; 48 49 /** 50 * <code>true</code> if the object has been modified since it was loaded from 51 * the server. In this case, on next upload, this object will be updated. 52 * Deleted objects are deleted from the server. If the objects are added (id=0), 53 * the modified is ignored and the object is added to the server. 54 */ 55 public boolean modified = false; 56 57 /** 58 * <code>true</code>, if the object has been deleted. 59 */ 60 public boolean deleted = false; 61 62 /** 63 * Visibility status as specified by the server. The visible attribute was 64 * introduced with the 0.4 API to be able to communicate deleted objects 65 * (they will have visible=false). Currently JOSM does never deal with 66 * these, so this is really for future use only. 67 */ 68 public boolean visible = true; 69 70 /** 71 * User that last modified this primitive, as specified by the server. 72 * Never changed by JOSM. 73 */ 74 public User user = null; 75 76 /** 77 * true if this object is considered "tagged". To be "tagged", an object 78 * must have one or more "non-standard" tags. "created_by" and "source" 79 * are typically considered "standard" tags and do not make an object 80 * "tagged". 81 */ 82 public boolean tagged = false; 83 84 /** 85 * true if this object has direction dependent tags (e.g. oneway) 86 */ 87 public boolean hasDirectionKeys = false; 88 89 /** 90 * If set to true, this object is currently selected. 91 */ 92 public volatile boolean selected = false; 93 94 /** 95 * Time of last modification to this object. This is not set by JOSM but 96 * read from the server and delivered back to the server unmodified. It is 97 * used to check against edit conflicts. 98 */ 99 public String timestamp = null; 100 101 /** 102 * The timestamp is only parsed when this is really necessary, and this 103 * is the cache for the result. 104 */ 105 public Date parsedTimestamp = null; 106 107 /** 108 * If set to true, this object is incomplete, which means only the id 109 * and type is known (type is the objects instance class) 110 */ 111 public boolean incomplete = false; 112 113 /** 114 * Contains the version number as returned by the API. Needed to 115 * ensure update consistency 116 */ 117 public int version = -1; 118 119 /** 120 * Contains a list of "uninteresting" keys that do not make an object 121 * "tagged". 122 * Initialized by checkTagged() 123 */ 124 private static Collection<String> uninteresting = null; 125 126 /** 127 * Contains a list of direction-dependent keys that make an object 128 * direction dependent. 129 * Initialized by checkDirectionTagged() 130 */ 131 private static Collection<String> directionKeys = null; 132 133 /** 134 * Implementation of the visitor scheme. Subclasses have to call the correct 135 * visitor function. 136 * @param visitor The visitor from which the visit() function must be called. 137 */ 138 abstract public void visit(Visitor visitor); 139 140 public final void delete(boolean deleted) { 141 this.deleted = deleted; 142 selected = false; 143 modified = true; 144 } 145 146 /** 147 * Returns the timestamp for this object, or the current time if none is set. 148 * Internally, parses the timestamp from XML into a Date object and caches it 149 * for possible repeated calls. 150 */ 151 public Date getTimestamp() { 152 if (parsedTimestamp == null) { 153 try { 154 parsedTimestamp = DateParser.parse(timestamp); 155 } catch (ParseException ex) { 156 parsedTimestamp = new Date(); 157 } 158 } 159 return parsedTimestamp; 160 } 161 162 /** 163 * Equal, if the id (and class) is equal. 164 * 165 * An primitive is equal to its incomplete counter part. 166 */ 167 @Override public boolean equals(Object obj) { 168 if (id == 0) return obj == this; 169 if (obj instanceof OsmPrimitive) { // not null too 170 return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass(); 171 } 172 return false; 173 } 174 175 /** 176 * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0. 177 * 178 * An primitive has the same hashcode as its incomplete counterpart. 179 */ 180 @Override public final int hashCode() { 181 if (id == 0) 182 return super.hashCode(); 183 final int[] ret = new int[1]; 184 Visitor v = new Visitor(){ 185 public void visit(Node n) { ret[0] = 1; } 186 public void visit(Way w) { ret[0] = 2; } 187 public void visit(Relation e) { ret[0] = 3; } 188 }; 189 visit(v); 190 return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0]; 191 } 192 193 /** 194 * Set the given value to the given key 195 * @param key The key, for which the value is to be set. 196 * @param value The value for the key. 197 */ 198 public final void put(String key, String value) { 199 if (value == null) 200 remove(key); 201 else { 202 if (keys == null) 203 keys = new HashMap<String, String>(); 204 keys.put(key, value); 205 } 206 checkTagged(); 207 checkDirectionTagged(); 208 } 209 /** 210 * Remove the given key from the list. 211 */ 212 public final void remove(String key) { 213 if (keys != null) { 214 keys.remove(key); 215 if (keys.isEmpty()) 216 keys = null; 217 } 218 checkTagged(); 219 checkDirectionTagged(); 220 } 221 222 public String getName() { 223 return null; 224 } 225 226 public final String get(String key) { 227 return keys == null ? null : keys.get(key); 228 } 229 230 public final Collection<Entry<String, String>> entrySet() { 231 if (keys == null) 232 return Collections.emptyList(); 233 return keys.entrySet(); 234 } 235 236 public final Collection<String> keySet() { 237 if (keys == null) 238 return Collections.emptyList(); 239 return keys.keySet(); 240 } 241 242 /** 243 * Get and write all attributes from the parameter. Does not fire any listener, so 244 * use this only in the data initializing phase 245 */ 246 public void cloneFrom(OsmPrimitive osm) { 247 keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys); 248 id = osm.id; 249 modified = osm.modified; 250 deleted = osm.deleted; 251 selected = osm.selected; 252 timestamp = osm.timestamp; 253 version = osm.version; 254 tagged = osm.tagged; 255 incomplete = osm.incomplete; 256 } 257 258 /** 259 * Perform an equality compare for all non-volatile fields not only for the id 260 * but for the whole object (for conflict resolving) 261 * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared 262 */ 263 public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 264 return 265 id == osm.id && 266 incomplete == osm.incomplete && 267 (semanticOnly || (modified == osm.modified)) && 268 deleted == osm.deleted && 269 (semanticOnly || (timestamp == null ? osm.timestamp==null : timestamp.equals(osm.timestamp))) && 270 (semanticOnly || (version==osm.version)) && 271 (semanticOnly || (user == null ? osm.user==null : user==osm.user)) && 272 (semanticOnly || (visible == osm.visible)) && 273 (keys == null ? osm.keys==null : keys.equals(osm.keys)); 274 } 275 276 public String getTimeStr() { 277 return timestamp == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp); 278 } 279 280 /** 281 * Updates the "tagged" flag. "keys" property should probably be made private 282 * to make sure this gets called when keys are set. 283 */ 284 public void checkTagged() { 285 tagged = false; 286 if(uninteresting == null) 287 uninteresting = new HashSet<String>(Arrays.asList(Main.pref.get("tags.uninteresting", 288 "source;note;converted_by;created_by").split(";"))); 289 if (keys != null) { 290 for (Entry<String,String> e : keys.entrySet()) { 291 if (!uninteresting.contains(e.getKey())) { 292 tagged = true; 293 break; 294 } 295 } 296 } 297 } 298 /** 299 * Updates the "hasDirectionKeys" flag. "keys" property should probably be made private 300 * to make sure this gets called when keys are set. 301 */ 302 public void checkDirectionTagged() { 303 hasDirectionKeys = false; 304 if(directionKeys == null) 305 directionKeys = new HashSet<String>(Arrays.asList(Main.pref.get("tags.direction", 306 "oneway;incline;incline_step;aerialway").split(";"))); 307 if (keys != null) { 308 for (Entry<String,String> e : keys.entrySet()) { 309 if (directionKeys.contains(e.getKey())) { 310 hasDirectionKeys = true; 311 break; 312 } 313 } 314 } 315 } 316 316 } -
trunk/src/org/openstreetmap/josm/data/osm/OsmUtils.java
r702 r1169 8 8 public class OsmUtils { 9 9 10 11 12 13 10 static ArrayList<String> TRUE_VALUES = new ArrayList<String>(Arrays 11 .asList(new String[] { "true", "yes", "1", "on" })); 12 static ArrayList<String> FALSE_VALUES = new ArrayList<String>(Arrays 13 .asList(new String[] { "false", "no", "0", "off" })); 14 14 15 16 15 public static final String trueval = "yes"; 16 public static final String falseval = "no"; 17 17 18 19 20 21 22 23 24 25 26 27 28 18 public static Boolean getOsmBoolean(String value) { 19 if(value == null) return null; 20 String lowerValue = value.toLowerCase(Locale.ENGLISH); 21 if (TRUE_VALUES.contains(lowerValue)) return Boolean.TRUE; 22 if (FALSE_VALUES.contains(lowerValue)) return Boolean.FALSE; 23 return null; 24 } 25 public static String getNamedOsmBoolean(String value) { 26 Boolean res = getOsmBoolean(value); 27 return res == null ? value : (res ? trueval : falseval); 28 } 29 29 } -
trunk/src/org/openstreetmap/josm/data/osm/Relation.java
r1162 r1169 11 11 /** 12 12 * An relation, having a set of tags and any number (0...n) of members. 13 * 13 * 14 14 * @author Frederik Ramm <frederik@remote.org> 15 15 */ 16 16 public final class Relation extends OsmPrimitive { 17 18 /**19 * All members of this relation. Note that after changing this,20 * makeBackReferences and/or removeBackReferences should be called.21 */22 public final List<RelationMember> members = new ArrayList<RelationMember>();23 17 24 @Override public void visit(Visitor visitor) { 25 visitor.visit(this); 26 } 18 /** 19 * All members of this relation. Note that after changing this, 20 * makeBackReferences and/or removeBackReferences should be called. 21 */ 22 public final List<RelationMember> members = new ArrayList<RelationMember>(); 27 23 28 /** 29 * Create an identical clone of the argument (including the id) 30 */ 31 public Relation(Relation clone) { 32 cloneFrom(clone); 33 } 34 35 /** 36 * Create an incomplete Relation. 37 */ 38 public Relation(long id) { 39 this.id = id; 40 incomplete = true; 41 } 42 43 /** 44 * Create an empty Relation. Use this only if you set meaningful values 45 * afterwards. 46 */ 47 public Relation() { 48 } 49 50 @Override public void cloneFrom(OsmPrimitive osm) { 51 super.cloneFrom(osm); 52 members.clear(); 53 // we must not add the members themselves, but instead 54 // add clones of the members 55 for (RelationMember em : ((Relation)osm).members) { 56 members.add(new RelationMember(em)); 57 } 58 } 24 @Override public void visit(Visitor visitor) { 25 visitor.visit(this); 26 } 59 27 60 @Override public String toString() { 61 // return "{Relation id="+id+" version="+version+" members="+Arrays.toString(members.toArray())+"}"; 62 // adding members in string increases memory usage a lot and overflows for looped relations 63 return "{Relation id="+id+" version="+version+"}"; 64 } 28 /** 29 * Create an identical clone of the argument (including the id) 30 */ 31 public Relation(Relation clone) { 32 cloneFrom(clone); 33 } 65 34 66 @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 67 return osm instanceof Relation ? super.realEqual(osm, semanticOnly) && members.equals(((Relation)osm).members) : false; 68 } 35 /** 36 * Create an incomplete Relation. 37 */ 38 public Relation(long id) { 39 this.id = id; 40 incomplete = true; 41 } 69 42 70 public int compareTo(OsmPrimitive o) { 71 return o instanceof Relation ? Long.valueOf(id).compareTo(o.id) : -1; 72 } 43 /** 44 * Create an empty Relation. Use this only if you set meaningful values 45 * afterwards. 46 */ 47 public Relation() { 48 } 73 49 74 public String getName() { 75 String name; 76 if (incomplete) { 77 name = tr("incomplete"); 78 } else { 79 name = get("type"); 80 // FIXME add names of members 81 if (name == null) 82 name = tr("relation"); 83 84 name += " ("; 85 String nameTag = get("name"); 86 if (nameTag == null) nameTag = get("ref"); 87 if (nameTag == null) nameTag = get("note"); 88 if (nameTag != null) name += "\"" + nameTag + "\", "; 89 int mbno = members.size(); 90 name += trn("{0} member", "{0} members", mbno, mbno) + ")"; 91 } 92 return name; 93 } 50 @Override public void cloneFrom(OsmPrimitive osm) { 51 super.cloneFrom(osm); 52 members.clear(); 53 // we must not add the members themselves, but instead 54 // add clones of the members 55 for (RelationMember em : ((Relation)osm).members) { 56 members.add(new RelationMember(em)); 57 } 58 } 94 59 95 public boolean isIncomplete() { 96 for (RelationMember m : members) 97 if (m.member == null) 98 return true; 99 return false; 100 } 60 @Override public String toString() { 61 // return "{Relation id="+id+" version="+version+" members="+Arrays.toString(members.toArray())+"}"; 62 // adding members in string increases memory usage a lot and overflows for looped relations 63 return "{Relation id="+id+" version="+version+"}"; 64 } 65 66 @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 67 return osm instanceof Relation ? super.realEqual(osm, semanticOnly) && members.equals(((Relation)osm).members) : false; 68 } 69 70 public int compareTo(OsmPrimitive o) { 71 return o instanceof Relation ? Long.valueOf(id).compareTo(o.id) : -1; 72 } 73 74 public String getName() { 75 String name; 76 if (incomplete) { 77 name = tr("incomplete"); 78 } else { 79 name = get("type"); 80 // FIXME add names of members 81 if (name == null) 82 name = tr("relation"); 83 84 name += " ("; 85 String nameTag = get("name"); 86 if (nameTag == null) nameTag = get("ref"); 87 if (nameTag == null) nameTag = get("note"); 88 if (nameTag != null) name += "\"" + nameTag + "\", "; 89 int mbno = members.size(); 90 name += trn("{0} member", "{0} members", mbno, mbno) + ")"; 91 } 92 return name; 93 } 94 95 public boolean isIncomplete() { 96 for (RelationMember m : members) 97 if (m.member == null) 98 return true; 99 return false; 100 } 101 101 } -
trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java
r627 r1169 2 2 3 3 /** 4 * A linkage class that can be used by an relation to keep a list of 4 * A linkage class that can be used by an relation to keep a list of 5 5 * members. Since membership may be qualified by a "role", a simple 6 6 * list is not sufficient. 7 * 7 * 8 8 * @author Frederik Ramm <frederik@remote.org> 9 9 */ 10 10 public class RelationMember { 11 11 12 public String role; 13 public OsmPrimitive member; 14 15 /** 16 * Default constructor. Does nothing. 17 */ 18 public RelationMember() { } 12 public String role; 13 public OsmPrimitive member; 19 14 20 public RelationMember(String role, OsmPrimitive member) { 21 this.role = role; 22 this.member = member; 23 } 24 25 /** 26 * Copy constructor. 27 * @param other relation member to be copied. 28 */ 29 public RelationMember(RelationMember other) { 30 role = other.role; 31 member = other.member; 32 } 33 34 @Override public boolean equals(Object other) { 35 if (!(other instanceof RelationMember)) return false; 36 RelationMember otherMember = (RelationMember) other; 37 return otherMember.role.equals(role) && otherMember.member.equals(member); 38 } 15 /** 16 * Default constructor. Does nothing. 17 */ 18 public RelationMember() { } 39 19 40 @Override public String toString() { 41 return '"' + role + "\"=" + member; 42 } 20 public RelationMember(String role, OsmPrimitive member) { 21 this.role = role; 22 this.member = member; 23 } 24 25 /** 26 * Copy constructor. 27 * @param other relation member to be copied. 28 */ 29 public RelationMember(RelationMember other) { 30 role = other.role; 31 member = other.member; 32 } 33 34 @Override public boolean equals(Object other) { 35 if (!(other instanceof RelationMember)) return false; 36 RelationMember otherMember = (RelationMember) other; 37 return otherMember.role.equals(role) && otherMember.member.equals(member); 38 } 39 40 @Override public String toString() { 41 return '"' + role + "\"=" + member; 42 } 43 43 } -
trunk/src/org/openstreetmap/josm/data/osm/TigerUtils.java
r627 r1169 5 5 import java.util.TreeSet; 6 6 7 /** 7 /** 8 8 * A simple class to keep helper functions for merging TIGER data 9 * 9 * 10 10 * @author daveh 11 11 * 12 12 */ 13 13 public class TigerUtils { 14 15 16 { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 14 15 public static boolean isTigerTag(String tag) 16 { 17 if (tag.indexOf("tiger:") == -1) 18 return false; 19 return true; 20 } 21 22 public static boolean tagIsInt(String name) { 23 if (name.equals("tiger:tlid")) 24 return true; 25 return false; 26 } 27 28 public static Object tagObj(String name) { 29 if (tagIsInt(name)) 30 return new Integer(name); 31 return name; 32 } 33 34 public static String combineTags(String name, Set<String> values) { 35 35 TreeSet<Object> resultSet = new TreeSet<Object>(); 36 36 for (String value: values) { 37 37 for (String part: value.split(":")) { 38 38 resultSet.add(tagObj(part)); 39 39 } … … 42 42 for (Object part : resultSet) { 43 43 if (combined.length() > 0) 44 45 44 combined += ":"; 45 combined += part; 46 46 } 47 48 49 50 51 52 53 54 55 47 return combined; 48 } 49 50 public static String combineTags(String name, String t1, String t2) { 51 Set<String> set = new TreeSet<String>(); 52 set.add(t1); 53 set.add(t2); 54 return TigerUtils.combineTags(name, set); 55 } 56 56 } -
trunk/src/org/openstreetmap/josm/data/osm/User.java
r655 r1169 4 4 import java.util.HashMap; 5 5 6 /** 6 /** 7 7 * A simple class to keep a list of user names. 8 * 8 * 9 9 * Instead of storing user names as strings with every OSM primitive, we store 10 10 * a reference to an user object, and make sure that for each username there 11 11 * is only one user object. 12 * 12 * 13 13 * @author fred 14 14 * … … 16 16 public class User { 17 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 18 /** storage for existing User objects. */ 19 private static HashMap<String,User> userMap = new HashMap<String,User>(); 20 21 /** the username. */ 22 public String name; 23 24 /** private constructor, only called from get method. */ 25 private User(String name) { 26 this.name = name; 27 } 28 29 /** returns a new or existing User object that represents the given name. */ 30 public static User get(String name) { 31 User user = userMap.get(name); 32 if (user == null) { 33 user = new User(name); 34 userMap.put(name, user); 35 } 36 return user; 37 } 38 38 } -
trunk/src/org/openstreetmap/josm/data/osm/Way.java
r755 r1169 21 21 public final class Way extends OsmPrimitive { 22 22 23 24 25 26 23 /** 24 * All way nodes in this way 25 */ 26 public final List<Node> nodes = new ArrayList<Node>(); 27 27 28 29 30 31 28 public void visitNodes(Visitor v) { 29 for (Node n : this.nodes) 30 v.visit(n); 31 } 32 32 33 34 35 36 33 public ArrayList<Pair<Node,Node>> getNodePairs(boolean sort) { 34 ArrayList<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>(); 35 Node lastN = null; 36 for (Node n : this.nodes) { 37 37 if (lastN == null) { 38 39 40 41 42 43 44 45 46 47 48 49 38 lastN = n; 39 continue; 40 } 41 Pair<Node,Node> np = new Pair<Node,Node>(lastN, n); 42 if (sort) { 43 Pair.sort(np); 44 } 45 chunkSet.add(np); 46 lastN = n; 47 } 48 return chunkSet; 49 } 50 50 51 51 52 53 54 52 @Override public void visit(Visitor visitor) { 53 visitor.visit(this); 54 } 55 55 56 57 58 59 60 cloneFrom(clone); 61 62 63 64 * Create an empty way without id. Use this only if you set meaningful 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 56 /** 57 * Create an identical clone of the argument (including the id) 58 */ 59 public Way(Way clone) { 60 cloneFrom(clone); 61 } 62 63 /** 64 * Create an empty way without id. Use this only if you set meaningful 65 * values yourself. 66 */ 67 public Way() { 68 } 69 70 /** 71 * Create an incomplete Way. 72 */ 73 public Way(long id) { 74 this.id = id; 75 incomplete = true; 76 } 77 78 @Override public void cloneFrom(OsmPrimitive osm) { 79 super.cloneFrom(osm); 80 nodes.clear(); 81 nodes.addAll(((Way)osm).nodes); 82 82 checkDirectionTagged(); 83 83 } 84 84 85 85 @Override public String toString() { … … 87 87 } 88 88 89 90 89 @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 90 return osm instanceof Way ? super.realEqual(osm, semanticOnly) && nodes.equals(((Way)osm).nodes) : false; 91 91 } 92 92 93 94 95 96 97 93 public int compareTo(OsmPrimitive o) { 94 if(o instanceof Relation) 95 return 1; 96 return o instanceof Way ? Long.valueOf(id).compareTo(o.id) : -1; 97 } 98 98 99 100 101 102 103 104 105 106 107 name = 108 109 110 111 112 99 public String getName() { 100 String name; 101 if (incomplete) { 102 name = tr("incomplete"); 103 } else { 104 name = get("name"); 105 if (name == null) name = get("ref"); 106 if (name == null) { 107 name = 108 (get("highway") != null) ? tr("highway") : 109 (get("railway") != null) ? tr("railway") : 110 (get("waterway") != null) ? tr("waterway") : 111 (get("landuse") != null) ? tr("landuse") : ""; 112 } 113 113 114 115 116 117 118 114 int nodesNo = new HashSet<Node>(nodes).size(); 115 name += trn(" ({0} node)", " ({0} nodes)", nodesNo, nodesNo); 116 } 117 return name; 118 } 119 119 120 121 122 123 120 @Deprecated 121 public boolean isIncomplete() { 122 return incomplete; 123 } 124 124 } -
trunk/src/org/openstreetmap/josm/data/osm/WaySegment.java
r627 r1169 6 6 */ 7 7 public final class WaySegment { 8 9 10 11 8 /** 9 * The way. 10 */ 11 public Way way; 12 12 13 14 15 16 17 13 /** 14 * The index of one of the 2 nodes in the way. The other node has the 15 * index <code>lowerIndex + 1</code>. 16 */ 17 public int lowerIndex; 18 18 19 20 21 22 19 public WaySegment(Way w, int i) { 20 way = w; 21 lowerIndex = i; 22 } 23 23 24 25 26 27 28 24 @Override public boolean equals(Object o) { 25 return o != null && o instanceof WaySegment 26 && ((WaySegment) o).way == way 27 && ((WaySegment) o).lowerIndex == lowerIndex; 28 } 29 29 30 31 32 30 @Override public int hashCode() { 31 return way.hashCode() ^ lowerIndex; 32 } 33 33 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/AddVisitor.java
r655 r1169 9 9 /** 10 10 * Visitor that adds the visited object to the dataset given at constructor. 11 * 11 * 12 12 * Is not capable of adding keys. 13 * 13 * 14 14 * @author imi 15 15 */ 16 16 public class AddVisitor implements Visitor { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 17 18 protected final DataSet ds; 19 20 public AddVisitor(DataSet ds) { 21 this.ds = ds; 22 } 23 24 public void visit(Node n) { 25 ds.nodes.add(n); 26 } 27 public void visit(Way w) { 28 ds.ways.add(w); 29 } 30 public void visit(Relation e) { 31 ds.relations.add(e); 32 } 33 33 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java
r627 r1169 13 13 /** 14 14 * Collect all nodes a specific osm primitive has. 15 * 15 * 16 16 * @author imi 17 17 */ 18 18 public class AllNodesVisitor implements Visitor { 19 19 20 21 22 23 20 /** 21 * The resulting nodes collected so far. 22 */ 23 public Collection<Node> nodes = new HashSet<Node>(); 24 24 25 26 27 28 29 30 25 /** 26 * Nodes have only itself as nodes. 27 */ 28 public void visit(Node n) { 29 nodes.add(n); 30 } 31 31 32 33 34 35 36 37 32 /** 33 * Ways have their way nodes. 34 */ 35 public void visit(Way w) { 36 w.visitNodes(this); 37 } 38 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 39 /** 40 * Relations may have any number of nodes. 41 * FIXME: do we want to collect nodes from segs/ways that are relation members? 42 * if so, use AutomatchVisitor! 43 */ 44 public void visit(Relation e) { 45 for (RelationMember m : e.members) 46 if (m.member instanceof Node) visit((Node)m.member); 47 } 48 /** 49 * @return All nodes the given primitive has. 50 */ 51 public static Collection<Node> getAllNodes(Collection<? extends OsmPrimitive> osms) { 52 AllNodesVisitor v = new AllNodesVisitor(); 53 for (OsmPrimitive osm : osms) 54 osm.visit(v); 55 return v.nodes; 56 } 57 57 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
r948 r1169 12 12 13 13 /** 14 * Calculates the total bounding rectangle of a series of {@link OsmPrimitive} objects, using the 14 * Calculates the total bounding rectangle of a series of {@link OsmPrimitive} objects, using the 15 15 * EastNorth values as reference. 16 16 * @author imi … … 61 61 62 62 /** 63 * Enlarges the calculated bounding box by 0.0001 degrees. 63 * Enlarges the calculated bounding box by 0.0001 degrees. 64 64 * If the bounding box has not been set (<code>min</code> or <code>max</code> 65 65 * equal <code>null</code>) this method does not do anything. 66 * 66 * 67 67 * @param enlargeDegree 68 68 */ … … 72 72 73 73 /** 74 * Enlarges the calculated bounding box by the specified number of degrees. 74 * Enlarges the calculated bounding box by the specified number of degrees. 75 75 * If the bounding box has not been set (<code>min</code> or <code>max</code> 76 76 * equal <code>null</code>) this method does not do anything. 77 * 77 * 78 78 * @param enlargeDegree 79 79 */ -
trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java
r627 r1169 14 14 /** 15 15 * Helper that collect all ways a node is part of. 16 * 16 * 17 17 * Deleted objects are not collected. 18 * 18 * 19 19 * @author imi 20 20 */ 21 21 public class CollectBackReferencesVisitor implements Visitor { 22 22 23 24 23 private final DataSet ds; 24 private final boolean indirectRefs; 25 25 26 27 28 29 26 /** 27 * The result list of primitives stored here. 28 */ 29 public final Collection<OsmPrimitive> data = new HashSet<OsmPrimitive>(); 30 30 31 31 32 33 34 35 36 37 38 39 32 /** 33 * Construct a back reference counter. 34 * @param ds The dataset to operate on. 35 */ 36 public CollectBackReferencesVisitor(DataSet ds) { 37 this.ds = ds; 38 this.indirectRefs = true; 39 } 40 40 41 42 43 44 41 public CollectBackReferencesVisitor(DataSet ds, boolean indirectRefs) { 42 this.ds = ds; 43 this.indirectRefs = indirectRefs; 44 } 45 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 // FIXME - this might be a candidate for optimisation 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 46 public void visit(Node n) { 47 for (Way w : ds.ways) { 48 if (w.deleted || w.incomplete) continue; 49 for (Node n2 : w.nodes) { 50 if (n == n2) { 51 data.add(w); 52 if (indirectRefs) { 53 visit(w); 54 } 55 } 56 } 57 } 58 checkRelationMembership(n); 59 } 60 61 public void visit(Way w) { 62 checkRelationMembership(w); 63 } 64 65 public void visit(Relation r) { 66 checkRelationMembership(r); 67 } 68 69 private void checkRelationMembership(OsmPrimitive p) { 70 // FIXME - this might be a candidate for optimisation 71 // if OSM primitives are made to hold a list of back 72 // references. 73 for (Relation r : ds.relations) { 74 if (r.incomplete || r.deleted) continue; 75 for (RelationMember m : r.members) { 76 if (m.member == p) { 77 if (!data.contains(r)) { 78 data.add(r); 79 if (indirectRefs) { 80 // move up the tree (there might be relations 81 // referring to this relation) 82 checkRelationMembership(r); 83 } 84 } 85 break; 86 } 87 } 88 } 89 } 90 90 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/CreateOsmChangeVisitor.java
r1071 r1169 16 16 /** 17 17 * Creates an OsmChange document from JOSM edits. 18 * See http://wiki.openstreetmap.org/index.php/OsmChange for a documentation of the 18 * See http://wiki.openstreetmap.org/index.php/OsmChange for a documentation of the 19 19 * OsmChange format. 20 * 20 * 21 21 * @author fred 22 22 * … … 30 30 StringWriter swriter; 31 31 OsmWriter osmwriter; 32 32 33 33 public CreateOsmChangeVisitor(Changeset changeset) { 34 34 writer = new PrintWriter(swriter = new StringWriter()); 35 35 writer.write("<osmChange version=\""); 36 36 writer.write(Main.pref.get("osm-server.version", "0.6")); 37 writer.write("\" generator=\"JOSM\">\n"); 37 writer.write("\" generator=\"JOSM\">\n"); 38 38 this.changeset = changeset; 39 39 osmwriter = new OsmWriter(writer, false, changeset); 40 40 } 41 41 42 42 public void visit(Node n) { 43 43 if (n.deleted) { … … 55 55 } 56 56 } 57 57 58 58 public void visit(Way w) { 59 59 if (w.deleted) { … … 69 69 switchMode((w.id == 0) ? "create" : "modify"); 70 70 w.visit(osmwriter); 71 } 71 } 72 72 } 73 73 74 74 public void visit(Relation r) { 75 75 if (r.deleted) { … … 85 85 switchMode((r.id == 0) ? "create" : "modify"); 86 86 r.visit(osmwriter); 87 } 87 } 88 88 } 89 89 90 90 private void switchMode(String newMode) { 91 91 if ((newMode != null && !newMode.equals(currentMode))||(newMode == null && currentMode != null)) { … … 105 105 } 106 106 } 107 107 108 108 public String getDocument() { 109 109 switchMode(null); 110 110 return swriter.toString() + "</osmChange>\n"; 111 111 } 112 112 113 113 public Map<OsmPrimitive,Long> getNewIdMap() { 114 114 return osmwriter.usedNewIds; -
trunk/src/org/openstreetmap/josm/data/osm/visitor/DeleteVisitor.java
r655 r1169 9 9 /** 10 10 * Visitor that adds the visited object to the dataset given at constructor. 11 * 11 * 12 12 * Is not capable of adding keys. 13 * 13 * 14 14 * @author imi 15 15 */ 16 16 public class DeleteVisitor implements Visitor { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 17 18 private final DataSet ds; 19 20 public DeleteVisitor(DataSet ds) { 21 this.ds = ds; 22 } 23 24 public void visit(Node n) { 25 ds.nodes.remove(n); 26 } 27 public void visit(Way w) { 28 ds.ways.remove(w); 29 } 30 public void visit(Relation e) { 31 ds.relations.remove(e); 32 } 33 33 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java
r1077 r1169 33 33 34 34 public class MapPaintVisitor extends SimplePaintVisitor { 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 35 protected boolean useRealWidth; 36 protected boolean zoomLevelDisplay; 37 protected boolean fillAreas; 38 protected int fillAlpha; 39 protected Color untaggedColor; 40 protected Color textColor; 41 protected boolean currentDashed = false; 42 protected int currentWidth = 0; 43 protected Stroke currentStroke = null; 44 protected Font orderFont; 45 protected ElemStyles styles; 46 protected double circum; 47 protected String regionalNameOrder[]; 48 49 protected boolean isZoomOk(ElemStyle e) { 50 if (!zoomLevelDisplay) /* show everything if the user wishes so */ 51 return true; 52 53 if(e == null) /* the default for things that don't have a rule (show, if scale is smaller than 1500m) */ 54 return (circum < 1500); 55 56 // formula to calculate a map scale: natural size / map size = scale 57 // example: 876000mm (876m as displayed) / 22mm (roughly estimated screen size of legend bar) = 39818 58 // 59 // so the exact "correcting value" below depends only on the screen size and resolution 60 // XXX - do we need a Preference setting for this (if things vary widely)? 61 return !(circum >= e.maxScale / 22 || circum < e.minScale / 22); 62 } 63 64 /** 65 * Draw a small rectangle. 66 * White if selected (as always) or red otherwise. 67 * 68 * @param n The node to draw. 69 */ 70 public void visit(Node n) { 71 IconElemStyle nodeStyle = styles.get(n); 72 if (nodeStyle != null && isZoomOk(nodeStyle)) 73 drawNode(n, nodeStyle.icon, nodeStyle.annotate); 74 else if (n.selected) 75 drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode); 76 else if (n.tagged) 77 drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode); 78 else 79 drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode); 80 } 81 82 /** 83 * Draw a line for all segments, according to tags. 84 * @param w The way to draw. 85 */ 86 public void visit(Way w) { 87 if(w.nodes.size() < 2) 88 return; 89 // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key 90 // (even if the tag is negated as in oneway=false) or the way is selected 91 boolean showDirection = w.selected || ((!useRealWidth) && (showDirectionArrow 92 && (!showRelevantDirectionsOnly || w.hasDirectionKeys))); 93 94 Color color = untaggedColor; 95 int width = defaultSegmentWidth; 96 int realWidth = 0; //the real width of the element in meters 97 boolean dashed = false; 98 ElemStyle wayStyle = styles.get(w); 99 100 if(!isZoomOk(wayStyle)) 101 return; 102 103 LineElemStyle l = null; 104 if(wayStyle!=null) 105 { 106 Color areacolor = untaggedColor; 107 boolean area = false; 108 if(wayStyle instanceof LineElemStyle) 109 l = (LineElemStyle)wayStyle; 110 else if (wayStyle instanceof AreaElemStyle) 111 { 112 areacolor = ((AreaElemStyle)wayStyle).color; 113 color = areacolor; 114 l = ((AreaElemStyle)wayStyle).line; 115 area = true; 116 } 117 if(l != null) 118 { 119 color = l.color; 120 width = l.width; 121 realWidth = l.realWidth; 122 dashed = l.dashed; 123 } 124 if (area && fillAreas) 125 drawWayAsArea(w, areacolor); 126 } 127 128 if (realWidth > 0 && useRealWidth && !showDirection) 129 { 130 int tmpWidth = (int) (100 / (float) (circum / realWidth)); 131 if (tmpWidth > width) width = tmpWidth; 132 } 133 if(w.selected) 134 color = selectedColor; 135 136 Node lastN; 137 if(l != null && l.overlays != null) 138 { 139 for(LineElemStyle s : l.overlays) 140 { 141 if(!s.over) 142 { 143 lastN = null; 144 for(Node n : w.nodes) 145 { 146 if(lastN != null) 147 { 148 drawSeg(lastN, n, s.color != null && !w.selected ? s.color : color, 149 false, s.getWidth(width), s.dashed); 150 } 151 lastN = n; 152 } 153 } 154 } 155 } 156 157 lastN = null; 158 for(Node n : w.nodes) 159 { 160 if(lastN != null) 161 drawSeg(lastN, n, color, showDirection, width, dashed); 162 lastN = n; 163 } 164 165 if(l != null && l.overlays != null) 166 { 167 for(LineElemStyle s : l.overlays) 168 { 169 if(s.over) 170 { 171 lastN = null; 172 for(Node n : w.nodes) 173 { 174 if(lastN != null) 175 { 176 drawSeg(lastN, n, s.color != null && !w.selected ? s.color : color, 177 false, s.getWidth(width), s.dashed); 178 } 179 lastN = n; 180 } 181 } 182 } 183 } 184 185 if(showOrderNumber) 186 { 187 int orderNumber = 0; 188 lastN = null; 189 for(Node n : w.nodes) 190 { 191 if(lastN != null) 192 { 193 orderNumber++; 194 drawOrderNumber(lastN, n, orderNumber); 195 } 196 lastN = n; 197 } 198 } 199 displaySegments(); 200 } 201 202 public void visit(Relation e) { 203 // relations are not (yet?) drawn. 204 } 205 206 // This assumes that all segments are aligned in the same direction! 207 protected void drawWayAsArea(Way w, Color color) 208 { 209 Polygon polygon = new Polygon(); 210 211 for (Node n : w.nodes) 212 { 213 Point p = nc.getPoint(n.eastNorth); 214 polygon.addPoint(p.x,p.y); 215 } 216 217 Color mycolor = w.selected ? selectedColor : color; 218 // set the opacity (alpha) level of the filled polygon 219 g.setColor(new Color( mycolor.getRed(), mycolor.getGreen(), mycolor.getBlue(), fillAlpha)); 220 221 g.fillPolygon(polygon); 222 } 223 224 // NEW 225 protected void drawNode(Node n, ImageIcon icon, boolean annotate) { 226 Point p = nc.getPoint(n.eastNorth); 227 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) || (p.y > nc.getHeight())) return; 228 int w = icon.getIconWidth(), h=icon.getIconHeight(); 229 icon.paintIcon ( Main.map.mapView, g, p.x-w/2, p.y-h/2 ); 230 String name = getNodeName(n); 231 if (name!=null && annotate) 232 { 233 g.setColor(textColor); 234 Font defaultFont = g.getFont(); 235 g.setFont (orderFont); 236 g.drawString (name, p.x+w/2+2, p.y+h/2+2); 237 g.setFont(defaultFont); 238 } 239 if (n.selected) 240 { 241 g.setColor ( selectedColor ); 242 g.drawRect (p.x-w/2-2,p.y-w/2-2, w+4, h+4); 243 } 244 } 245 246 protected String getNodeName(Node n) { 247 String name = null; 248 if (n.keys != null) { 249 for (int i = 0; i < regionalNameOrder.length; i++) { 250 name = n.keys.get(regionalNameOrder[i]); 251 if (name != null) break; 252 } 253 } 254 return name; 255 } 256 257 private void drawSeg(Node n1, Node n2, Color col, boolean showDirection, int width, boolean dashed) { 258 if (col != currentColor || width != currentWidth || dashed != currentDashed) { 259 displaySegments(col, width, dashed); 260 } 261 Point p1 = nc.getPoint(n1.eastNorth); 262 Point p2 = nc.getPoint(n2.eastNorth); 263 264 if (!isSegmentVisible(p1, p2)) { 265 return; 266 } 267 currentPath.moveTo(p1.x, p1.y); 268 currentPath.lineTo(p2.x, p2.y); 269 270 if (showDirection) { 271 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI; 272 currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI))); 273 currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI))); 274 currentPath.lineTo(p2.x, p2.y); 275 } 276 } 277 278 protected void displaySegments() { 279 displaySegments(null, 0, false); 280 } 281 282 protected void displaySegments(Color newColor, int newWidth, boolean newDash) { 283 if (currentPath != null) { 284 Graphics2D g2d = (Graphics2D)g; 285 g2d.setColor(inactive ? inactiveColor : currentColor); 286 if (currentStroke == null) { 287 if (currentDashed) 288 g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,0,new float[] {9},0)); 289 else 290 g2d.setStroke(new BasicStroke(currentWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); 291 } 292 g2d.draw(currentPath); 293 g2d.setStroke(new BasicStroke(1)); 294 295 currentPath = new GeneralPath(); 296 currentColor = newColor; 297 currentWidth = newWidth; 298 currentDashed = newDash; 299 currentStroke = null; 300 } 301 } 302 303 /** 304 * Draw the node as small rectangle with the given color. 305 * 306 * @param n The node to draw. 307 * @param color The color of the node. 308 */ 309 public void drawNode(Node n, Color color, int size, int radius, boolean fill) { 310 if (isZoomOk(null) && size > 1) { 311 Point p = nc.getPoint(n.eastNorth); 312 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) 313 || (p.y > nc.getHeight())) 314 return; 315 g.setColor(color); 316 if (fill) { 317 g.fillRect(p.x - radius, p.y - radius, size, size); 318 g.drawRect(p.x - radius, p.y - radius, size, size); 319 } else 320 g.drawRect(p.x - radius, p.y - radius, size, size); 321 } 322 } 323 324 // NW 111106 Overridden from SimplePaintVisitor in josm-1.4-nw1 325 // Shows areas before non-areas 326 public void visitAll(DataSet data, Boolean virtual) { 327 getSettings(virtual); 328 untaggedColor = Main.pref.getColor(marktr("untagged"),Color.GRAY); 329 textColor = Main.pref.getColor (marktr("text"), Color.WHITE); 330 useRealWidth = Main.pref.getBoolean("mappaint.useRealWidth",false); 331 zoomLevelDisplay = Main.pref.getBoolean("mappaint.zoomLevelDisplay",false); 332 fillAreas = Main.pref.getBoolean("mappaint.fillareas", true); 333 fillAlpha = Math.min(255, Math.max(0, Integer.valueOf(Main.pref.getInteger("mappaint.fillalpha", 50)))); 334 circum = Main.map.mapView.getScale()*100*Main.proj.scaleFactor()*40041455; // circumference of the earth in meter 335 styles = MapPaintStyles.getStyles(); 336 orderFont = new Font(Main.pref.get("mappaint.font","Helvetica"), Font.PLAIN, Main.pref.getInteger("mappaint.fontsize", 8)); 337 String currentLocale = Locale.getDefault().getLanguage(); 338 regionalNameOrder = Main.pref.get("mappaint.nameOrder", "name:"+currentLocale+";name;int_name").split(";"); 339 340 if (styles.hasAreas()) { 341 Collection<Way> noAreaWays = new LinkedList<Way>(); 342 343 for (final OsmPrimitive osm : data.ways) 344 if (!osm.incomplete && !osm.deleted && styles.isArea((Way)osm)) 345 osm.visit(this); 346 else if (!osm.deleted && !osm.incomplete) 347 noAreaWays.add((Way)osm); 348 349 for (final OsmPrimitive osm : noAreaWays) 350 osm.visit(this); 351 } 352 else 353 { 354 for (final OsmPrimitive osm : data.ways) 355 if (!osm.incomplete && !osm.deleted) 356 osm.visit(this); 357 } 358 359 for (final OsmPrimitive osm : data.getSelected()) 360 if (!osm.incomplete && !osm.deleted){ 361 osm.visit(this); 362 } 363 364 displaySegments(); 365 366 for (final OsmPrimitive osm : data.nodes) 367 if (!osm.incomplete && !osm.deleted) 368 osm.visit(this); 369 370 if (virtualNodeSize != 0) 371 { 372 currentColor = nodeColor; 373 for (final OsmPrimitive osm : data.ways) 374 if (!osm.deleted) 375 visitVirtual((Way)osm); 376 displaySegments(null); 377 } 378 } 379 380 /** 381 * Draw a number of the order of the two consecutive nodes within the 382 * parents way 383 */ 384 protected void drawOrderNumber(Node n1, Node n2, int orderNumber) { 385 Point p1 = nc.getPoint(n1.eastNorth); 386 Point p2 = nc.getPoint(n2.eastNorth); 387 drawOrderNumber(p1, p2, orderNumber); 388 } 389 389 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java
r627 r1169 19 19 * A visitor that get a data set at construction time and merge every visited object 20 20 * into it. 21 * 21 * 22 22 * @author imi 23 23 */ 24 24 public class MergeVisitor implements Visitor { 25 25 26 27 * Map from primitives in the database to visited primitives. (Attention: The other way 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 } 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 26 /** 27 * Map from primitives in the database to visited primitives. (Attention: The other way 28 * round than merged) 29 */ 30 public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>(); 31 32 private final DataSet ds; 33 private final DataSet mergeds; 34 35 private final HashMap<Long, Node> nodeshash = new HashMap<Long, Node>(); 36 private final HashMap<Long, Way> wayshash = new HashMap<Long, Way>(); 37 private final HashMap<Long, Relation> relshash = new HashMap<Long, Relation>(); 38 39 /** 40 * A list of all primitives that got replaced with other primitives. 41 * Key is the primitives in the other's dataset and the value is the one that is now 42 * in ds.nodes instead. 43 */ 44 private final Map<OsmPrimitive, OsmPrimitive> merged 45 = new HashMap<OsmPrimitive, OsmPrimitive>(); 46 47 public MergeVisitor(DataSet ds, DataSet mergeds) { 48 this.ds = ds; 49 this.mergeds = mergeds; 50 51 for (Node n : ds.nodes) if (n.id != 0) nodeshash.put(n.id, n); 52 for (Way w : ds.ways) if (w.id != 0) wayshash.put(w.id, w); 53 for (Relation r : ds.relations) if (r.id != 0) relshash.put(r.id, r); 54 } 55 56 private <P extends OsmPrimitive> void genMerge(P other, 57 Collection<P> myprims, Collection<P> mergeprims, 58 HashMap<Long, P> primhash) { 59 // 1. Try to find an identical prim with the same id. 60 if (mergeAfterId(myprims, primhash, other)) 61 return; 62 63 // 2. Try to find a prim we can merge with the prim from the other ds. 64 for (P my : myprims) { 65 // LinkedList.contains calls equal, and OsmPrimitive.equal 66 // compares just the id. 67 if (match(my, other) && !mergeprims.contains(my)) { 68 merged.put(other, my); 69 mergeCommon(my, other); 70 return; 71 } 72 } 73 74 // 3. No idea how to merge that. Simply add it unchanged. 75 myprims.add(other); 76 } 77 78 public void visit(Node other) { 79 genMerge(other, ds.nodes, mergeds.nodes, nodeshash); 80 } 81 82 public void visit(Way other) { 83 fixWay(other); 84 genMerge(other, ds.ways, mergeds.ways, wayshash); 85 } 86 87 public void visit(Relation other) { 88 fixRelation(other); 89 genMerge(other, ds.relations, mergeds.relations, relshash); 90 } 91 92 /** 93 * Postprocess the dataset and fix all merged references to point to the actual 94 * data. 95 */ 96 public void fixReferences() { 97 for (Way w : ds.ways) fixWay(w); 98 for (Relation r : ds.relations) fixRelation(r); 99 for (OsmPrimitive osm : conflicts.values()) 100 if (osm instanceof Way) 101 fixWay((Way)osm); 102 else if (osm instanceof Relation) 103 fixRelation((Relation) osm); 104 } 105 106 private void fixWay(Way w) { 107 boolean replacedSomething = false; 108 LinkedList<Node> newNodes = new LinkedList<Node>(); 109 for (Node n : w.nodes) { 110 Node otherN = (Node) merged.get(n); 111 newNodes.add(otherN == null ? n : otherN); 112 if (otherN != null) 113 replacedSomething = true; 114 } 115 if (replacedSomething) { 116 w.nodes.clear(); 117 w.nodes.addAll(newNodes); 118 } 119 } 120 121 private void fixRelation(Relation r) { 122 boolean replacedSomething = false; 123 LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>(); 124 for (RelationMember m : r.members) { 125 OsmPrimitive otherP = merged.get(m.member); 126 if (otherP == null) { 127 newMembers.add(m); 128 } else { 129 RelationMember mnew = new RelationMember(m); 130 mnew.member = otherP; 131 newMembers.add(mnew); 132 replacedSomething = true; 133 } 134 } 135 if (replacedSomething) { 136 r.members.clear(); 137 r.members.addAll(newMembers); 138 } 139 } 140 141 private static <P extends OsmPrimitive> boolean match(P p1, P p2) { 142 if ((p1.id == 0 || p2.id == 0) && !p1.incomplete && !p2.incomplete) { 143 return realMatch(p1, p2); 144 } 145 return p1.id == p2.id; 146 } 147 148 /** @return true if the prims have pretty much the same data, i.e. the 149 * same position, the same members, ... 150 */ 151 // Java cannot dispatch on generics... 152 private static boolean realMatch(OsmPrimitive p1, OsmPrimitive p2) { 153 if (p1 instanceof Node && p2 instanceof Node) { 154 return realMatch((Node) p1, (Node) p2); 155 } else if (p1 instanceof Way && p2 instanceof Way) { 156 return realMatch((Way) p1, (Way) p2); 157 } else if (p1 instanceof Relation && p2 instanceof Relation) { 158 return realMatch((Relation) p1, (Relation) p2); 159 } else { 160 throw new RuntimeException("arguments have unknown type"); 161 } 162 } 163 164 private static boolean realMatch(Node n1, Node n2) { 165 return n1.coor.equalsEpsilon(n2.coor); 166 } 167 168 private static boolean realMatch(Way w1, Way w2) { 169 if (w1.nodes.size() != w2.nodes.size()) 170 return false; 171 Iterator<Node> it = w1.nodes.iterator(); 172 for (Node n : w2.nodes) 173 if (!match(n, it.next())) 174 return false; 175 return true; 176 } 177 178 private static boolean realMatch(Relation w1, Relation w2) { 179 // FIXME this is not perfect yet... 180 if (w1.members.size() != w2.members.size()) 181 return false; 182 for (RelationMember em : w1.members) { 183 if (!w2.members.contains(em)) { 184 return false; 185 } 186 } 187 return true; 188 } 189 190 /** 191 * Merge the common parts of an osm primitive. 192 * @param my The object, the information gets merged into 193 * @param other The object, the information gets merged from 194 */ 195 private void mergeCommon(OsmPrimitive my, OsmPrimitive other) { 196 if (other.deleted) 197 my.delete(true); 198 if (my.id == 0 || !my.modified || other.modified) { 199 if (my.id == 0 && other.id != 0) { 200 my.id = other.id; 201 my.modified = other.modified; // match a new node 202 } else if (my.id != 0 && other.id != 0 && other.modified) 203 my.modified = true; 204 } 205 if (other.keys == null) 206 return; 207 if (my.keySet().containsAll(other.keys.keySet())) 208 return; 209 if (my.keys == null) 210 my.keys = other.keys; 211 else 212 my.keys.putAll(other.keys); 213 214 my.modified = true; 215 } 216 217 /** 218 * @return <code>true</code>, if no merge is needed or merge is performed already. 219 */ 220 private <P extends OsmPrimitive> boolean mergeAfterId( 221 Collection<P> primitives, HashMap<Long, P> hash, P other) { 222 // Fast-path merging of identical objects 223 if (hash.containsKey(other.id)) { 224 P my = hash.get(other.id); 225 if (my.realEqual(other, true)) { 226 merged.put(other, my); 227 return true; 228 } 229 } 230 231 for (P my : primitives) { 232 if (my.realEqual(other, false)) { 233 merged.put(other, my); 234 return true; // no merge needed. 235 } 236 if (my.realEqual(other, true)) { 237 Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp(); 238 Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp(); 239 240 // they differ in modified/timestamp combination only. Auto-resolve it. 241 merged.put(other, my); 242 if (myd.before(otherd)) { 243 my.modified = other.modified; 244 my.timestamp = other.timestamp; 245 } 246 return true; // merge done. 247 } 248 if (my.id == other.id && my.id != 0) { 249 Date myd = my.timestamp == null ? new Date(0) : my.getTimestamp(); 250 Date otherd = other.timestamp == null ? new Date(0) : other.getTimestamp(); 251 252 if (my.incomplete || other.incomplete) { 253 if (my.incomplete) { 254 my.cloneFrom(other); 255 } 256 } else if (my.modified && other.modified) { 257 conflicts.put(my, other); 258 } else if (!my.modified && !other.modified) { 259 if (myd.before(otherd)) { 260 my.cloneFrom(other); 261 } 262 } else if (other.modified) { 263 if (myd.after(otherd)) { 264 conflicts.put(my, other); 265 } else { 266 my.cloneFrom(other); 267 } 268 } else if (my.modified) { 269 if (myd.before(otherd)) { 270 conflicts.put(my, other); 271 } 272 } 273 merged.put(other, my); 274 return true; 275 } 276 } 277 return false; 278 } 279 279 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java
r756 r1169 17 17 /** 18 18 * Able to create a name and an icon for each data element. 19 * 19 * 20 20 * @author imi 21 21 */ 22 22 public class NameVisitor implements Visitor { 23 23 24 /** 25 * The name of the item class 26 */ 27 public String className; 28 public String classNamePlural; 29 /** 30 * The name of this item. 31 */ 32 public String name; 33 /** 34 * The icon of this item. 35 */ 36 public Icon icon; 37 38 /** 39 * If the node has a name-key or id-key, this is displayed. If not, (lat,lon) 40 * is displayed. 41 */ 42 public void visit(Node n) { 43 name = n.getName(); 44 addId(n); 45 icon = ImageProvider.get("data", "node"); 46 className = "node"; 47 classNamePlural = trn("node", "nodes", 2); 48 } 24 /** 25 * The name of the item class 26 */ 27 public String className; 28 public String classNamePlural; 29 /** 30 * The name of this item. 31 */ 32 public String name; 33 /** 34 * The icon of this item. 35 */ 36 public Icon icon; 49 37 50 /** 51 * If the way has a name-key or id-key, this is displayed. If not, (x nodes) 52 * is displayed with x being the number of nodes in the way. 53 */ 54 public void visit(Way w) { 55 name = w.getName(); 56 addId(w); 57 icon = ImageProvider.get("data", "way"); 58 className = "way"; 59 classNamePlural = trn("way", "ways", 2); 60 } 61 62 /** 63 */ 64 public void visit(Relation e) { 65 name = e.getName(); 66 addId(e); 67 icon = ImageProvider.get("data", "relation"); 68 className = "relation"; 69 classNamePlural = trn("relation", "relations", 2); 70 } 71 72 public JLabel toLabel() { 73 return new JLabel(name, icon, JLabel.HORIZONTAL); 74 } 38 /** 39 * If the node has a name-key or id-key, this is displayed. If not, (lat,lon) 40 * is displayed. 41 */ 42 public void visit(Node n) { 43 name = n.getName(); 44 addId(n); 45 icon = ImageProvider.get("data", "node"); 46 className = "node"; 47 classNamePlural = trn("node", "nodes", 2); 48 } 49 50 /** 51 * If the way has a name-key or id-key, this is displayed. If not, (x nodes) 52 * is displayed with x being the number of nodes in the way. 53 */ 54 public void visit(Way w) { 55 name = w.getName(); 56 addId(w); 57 icon = ImageProvider.get("data", "way"); 58 className = "way"; 59 classNamePlural = trn("way", "ways", 2); 60 } 61 62 /** 63 */ 64 public void visit(Relation e) { 65 name = e.getName(); 66 addId(e); 67 icon = ImageProvider.get("data", "relation"); 68 className = "relation"; 69 classNamePlural = trn("relation", "relations", 2); 70 } 71 72 public JLabel toLabel() { 73 return new JLabel(name, icon, JLabel.HORIZONTAL); 74 } 75 75 76 76 77 78 79 77 private void addId(OsmPrimitive osm) { 78 if (Main.pref.getBoolean("osm-primitives.showid")) 79 name += tr(" [id: {0}]", osm.id); 80 80 } 81 81 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java
r999 r1169 32 32 public class SimplePaintVisitor implements Visitor { 33 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 * @param nThe node to draw.310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 34 public final static Color darkerblue = new Color(0,0,96); 35 public final static Color darkblue = new Color(0,0,128); 36 public final static Color darkgreen = new Color(0,128,0); 37 public final static Color teal = new Color(0,128,128); 38 39 /** 40 * The environment to paint to. 41 */ 42 protected Graphics g; 43 /** 44 * MapView to get screen coordinates. 45 */ 46 protected NavigatableComponent nc; 47 48 public boolean inactive; 49 50 protected static final double PHI = Math.toRadians(20); 51 52 /** 53 * Preferences 54 */ 55 protected Color inactiveColor; 56 protected Color selectedColor; 57 protected Color nodeColor; 58 protected Color dfltWayColor; 59 protected Color relationColor; 60 protected Color untaggedWayColor; 61 protected Color incompleteColor; 62 protected Color backgroundColor; 63 protected boolean showDirectionArrow; 64 protected boolean showRelevantDirectionsOnly; 65 protected boolean showOrderNumber; 66 protected boolean fillSelectedNode; 67 protected boolean fillUnselectedNode; 68 protected int selectedNodeRadius; 69 protected int unselectedNodeRadius; 70 protected int selectedNodeSize; 71 protected int unselectedNodeSize; 72 protected int defaultSegmentWidth; 73 protected int virtualNodeSize; 74 protected int virtualNodeSpace; 75 protected int segmentNumberSpace; 76 protected int taggedNodeRadius; 77 protected int taggedNodeSize; 78 79 /** 80 * Draw subsequent segments of same color as one Path 81 */ 82 protected Color currentColor = null; 83 protected GeneralPath currentPath = new GeneralPath(); 84 85 Rectangle bbox = new Rectangle(); 86 87 protected void getSettings(Boolean virtual) { 88 inactiveColor = Main.pref.getColor(marktr("inactive"), Color.DARK_GRAY); 89 selectedColor = Main.pref.getColor(marktr("selected"), Color.WHITE); 90 nodeColor = Main.pref.getColor(marktr("node"), Color.RED); 91 dfltWayColor = Main.pref.getColor(marktr("way"), darkblue); 92 relationColor = Main.pref.getColor(marktr("relation"), teal); 93 untaggedWayColor = Main.pref.getColor(marktr("untagged way"), darkgreen); 94 incompleteColor = Main.pref.getColor(marktr("incomplete way"), darkerblue); 95 backgroundColor = Main.pref.getColor(marktr("background"), Color.BLACK); 96 showDirectionArrow = Main.pref.getBoolean("draw.segment.direction"); 97 showRelevantDirectionsOnly = Main.pref.getBoolean("draw.segment.relevant_directions_only"); 98 showOrderNumber = Main.pref.getBoolean("draw.segment.order_number"); 99 selectedNodeRadius = Main.pref.getInteger("mappaint.node.selected-size", 5) / 2; 100 selectedNodeSize = selectedNodeRadius * 2; 101 unselectedNodeRadius = Main.pref.getInteger("mappaint.node.unselected-size", 3) / 2; 102 unselectedNodeSize = unselectedNodeRadius * 2; 103 taggedNodeRadius = Main.pref.getInteger("mappaint.node.tagged-size", 5) / 2; 104 taggedNodeSize = taggedNodeRadius * 2; 105 defaultSegmentWidth = Main.pref.getInteger("mappaint.segment.default-width", 2); 106 fillSelectedNode = Main.pref.getBoolean("mappaint.node.fill-selected", true); 107 fillUnselectedNode = Main.pref.getBoolean("mappaint.node.fill-unselected", false); 108 virtualNodeSize = virtual ? Main.pref.getInteger("mappaint.node.virtual-size", 8) / 2 : 0; 109 virtualNodeSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70); 110 segmentNumberSpace = Main.pref.getInteger("mappaint.segmentnumber.space", 40); 111 112 ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, 113 Main.pref.getBoolean("mappaint.use-antialiasing", false) ? 114 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); 115 } 116 117 public void visitAll(DataSet data, Boolean virtual) { 118 getSettings(virtual); 119 // draw tagged ways first, then untagged ways. takes 120 // time to iterate through list twice, OTOH does not 121 // require changing the colour while painting... 122 for (final OsmPrimitive osm : data.relations) 123 if (!osm.deleted && !osm.selected) 124 osm.visit(this); 125 126 for (final OsmPrimitive osm : data.ways) 127 if (!osm.deleted && !osm.selected && osm.tagged) 128 osm.visit(this); 129 displaySegments(); 130 131 for (final OsmPrimitive osm : data.ways) 132 if (!osm.deleted && !osm.selected && !osm.tagged) 133 osm.visit(this); 134 displaySegments(); 135 136 for (final OsmPrimitive osm : data.getSelected()) 137 if (!osm.deleted) 138 osm.visit(this); 139 displaySegments(); 140 141 for (final OsmPrimitive osm : data.nodes) 142 if (!osm.deleted && !osm.selected) 143 osm.visit(this); 144 if(virtualNodeSize != 0) 145 { 146 currentColor = nodeColor; 147 for (final OsmPrimitive osm : data.ways) 148 if (!osm.deleted) 149 visitVirtual((Way)osm); 150 displaySegments(); 151 } 152 } 153 154 /** 155 * Draw a small rectangle. 156 * White if selected (as always) or red otherwise. 157 * 158 * @param n The node to draw. 159 */ 160 public void visit(Node n) { 161 if (n.incomplete) return; 162 163 if (inactive) 164 drawNode(n, inactiveColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode); 165 else if (n.selected) 166 drawNode(n, selectedColor, selectedNodeSize, selectedNodeRadius, fillSelectedNode); 167 else if(n.tagged) 168 drawNode(n, nodeColor, taggedNodeSize, taggedNodeRadius, fillUnselectedNode); 169 else 170 drawNode(n, nodeColor, unselectedNodeSize, unselectedNodeRadius, fillUnselectedNode); 171 } 172 173 public static Boolean isLargeSegment(Point p1, Point p2, int space) 174 { 175 int xd = p1.x-p2.x; if(xd < 0) xd = -xd; 176 int yd = p1.y-p2.y; if(yd < 0) yd = -yd; 177 return (xd+yd > space); 178 } 179 180 public void visitVirtual(Way w) { 181 Iterator<Node> it = w.nodes.iterator(); 182 if (it.hasNext()) { 183 Point lastP = nc.getPoint(it.next().eastNorth); 184 while(it.hasNext()) 185 { 186 Point p = nc.getPoint(it.next().eastNorth); 187 if(isSegmentVisible(lastP, p) && isLargeSegment(lastP, p, virtualNodeSpace)) 188 { 189 int x = (p.x+lastP.x)/2; 190 int y = (p.y+lastP.y)/2; 191 currentPath.moveTo(x-virtualNodeSize, y); 192 currentPath.lineTo(x+virtualNodeSize, y); 193 currentPath.moveTo(x, y-virtualNodeSize); 194 currentPath.lineTo(x, y+virtualNodeSize); 195 } 196 lastP = p; 197 } 198 } 199 } 200 201 /** 202 * Draw a darkblue line for all segments. 203 * @param w The way to draw. 204 */ 205 public void visit(Way w) { 206 if (w.incomplete || w.nodes.size() < 2) 207 return; 208 209 // show direction arrows, if draw.segment.relevant_directions_only is not set, the way is tagged with a direction key 210 // (even if the tag is negated as in oneway=false) or the way is selected 211 212 boolean showThisDirectionArrow = w.selected 213 || (showDirectionArrow && (!showRelevantDirectionsOnly || w.hasDirectionKeys)); 214 Color wayColor; 215 216 if (inactive) { 217 wayColor = inactiveColor; 218 } else if (!w.tagged) { 219 wayColor = untaggedWayColor; 220 } else { 221 wayColor = dfltWayColor; 222 } 223 224 Iterator<Node> it = w.nodes.iterator(); 225 if (it.hasNext()) { 226 Point lastP = nc.getPoint(it.next().eastNorth); 227 for (int orderNumber = 1; it.hasNext(); orderNumber++) { 228 Point p = nc.getPoint(it.next().eastNorth); 229 drawSegment(lastP, p, w.selected && !inactive ? selectedColor : wayColor, showThisDirectionArrow); 230 if (showOrderNumber) 231 drawOrderNumber(lastP, p, orderNumber); 232 lastP = p; 233 } 234 } 235 } 236 237 private Stroke relatedWayStroke = new BasicStroke( 238 4, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL); 239 public void visit(Relation r) { 240 if (r.incomplete) return; 241 242 Color col; 243 if (inactive) { 244 col = inactiveColor; 245 } else if (r.selected) { 246 col = selectedColor; 247 } else { 248 col = relationColor; 249 } 250 g.setColor(col); 251 252 for (RelationMember m : r.members) { 253 if (m.member.incomplete || m.member.deleted) continue; 254 255 if (m.member instanceof Node) { 256 Point p = nc.getPoint(((Node) m.member).eastNorth); 257 if (p.x < 0 || p.y < 0 258 || p.x > nc.getWidth() || p.y > nc.getHeight()) continue; 259 260 g.drawOval(p.x-3, p.y-3, 6, 6); 261 } else if (m.member instanceof Way) { 262 GeneralPath path = new GeneralPath(); 263 264 boolean first = true; 265 for (Node n : ((Way) m.member).nodes) { 266 if (n.incomplete || n.deleted) continue; 267 Point p = nc.getPoint(n.eastNorth); 268 if (first) { 269 path.moveTo(p.x, p.y); 270 first = false; 271 } else { 272 path.lineTo(p.x, p.y); 273 } 274 } 275 276 ((Graphics2D) g).draw(relatedWayStroke.createStrokedShape(path)); 277 } 278 } 279 } 280 281 /** 282 * Draw an number of the order of the two consecutive nodes within the 283 * parents way 284 */ 285 protected void drawOrderNumber(Point p1, Point p2, int orderNumber) { 286 if (isSegmentVisible(p1, p2) && isLargeSegment(p1, p2, segmentNumberSpace)) { 287 String on = Integer.toString(orderNumber); 288 int strlen = on.length(); 289 int x = (p1.x+p2.x)/2 - 4*strlen; 290 int y = (p1.y+p2.y)/2 + 4; 291 292 if(virtualNodeSize != 0 && isLargeSegment(p1, p2, virtualNodeSpace)) 293 { 294 y = (p1.y+p2.y)/2 - virtualNodeSize - 3; 295 } 296 297 displaySegments(); // draw nodes on top! 298 Color c = g.getColor(); 299 g.setColor(backgroundColor); 300 g.fillRect(x-1, y-12, 8*strlen+1, 14); 301 g.setColor(c); 302 g.drawString(on, x, y); 303 } 304 } 305 306 /** 307 * Draw the node as small rectangle with the given color. 308 * 309 * @param n The node to draw. 310 * @param color The color of the node. 311 */ 312 public void drawNode(Node n, Color color, int size, int radius, boolean fill) { 313 if (size > 1) { 314 Point p = nc.getPoint(n.eastNorth); 315 if ((p.x < 0) || (p.y < 0) || (p.x > nc.getWidth()) 316 || (p.y > nc.getHeight())) 317 return; 318 g.setColor(color); 319 if (fill) { 320 g.fillRect(p.x - radius, p.y - radius, size, size); 321 g.drawRect(p.x - radius, p.y - radius, size, size); 322 } else 323 g.drawRect(p.x - radius, p.y - radius, size, size); 324 } 325 } 326 327 /** 328 * Draw a line with the given color. 329 */ 330 protected void drawSegment(Point p1, Point p2, Color col, boolean showDirection) { 331 if (col != currentColor) displaySegments(col); 332 333 if (isSegmentVisible(p1, p2)) { 334 currentPath.moveTo(p1.x, p1.y); 335 currentPath.lineTo(p2.x, p2.y); 336 337 if (showDirection) { 338 double t = Math.atan2(p2.y-p1.y, p2.x-p1.x) + Math.PI; 339 currentPath.lineTo((int)(p2.x + 10*Math.cos(t-PHI)), (int)(p2.y + 10*Math.sin(t-PHI))); 340 currentPath.moveTo((int)(p2.x + 10*Math.cos(t+PHI)), (int)(p2.y + 10*Math.sin(t+PHI))); 341 currentPath.lineTo(p2.x, p2.y); 342 } 343 } 344 } 345 346 protected boolean isSegmentVisible(Point p1, Point p2) { 347 if ((p1.x < 0) && (p2.x < 0)) return false; 348 if ((p1.y < 0) && (p2.y < 0)) return false; 349 if ((p1.x > nc.getWidth()) && (p2.x > nc.getWidth())) return false; 350 if ((p1.y > nc.getHeight()) && (p2.y > nc.getHeight())) return false; 351 return true; 352 } 353 354 public void setGraphics(Graphics g) { 355 this.g = g; 356 } 357 358 public void setNavigatableComponent(NavigatableComponent nc) { 359 this.nc = nc; 360 } 361 362 protected void displaySegments() { 363 displaySegments(null); 364 } 365 protected void displaySegments(Color newColor) { 366 if (currentPath != null) { 367 g.setColor(currentColor); 368 ((Graphics2D) g).draw(currentPath); 369 currentPath = new GeneralPath(); 370 currentColor = newColor; 371 } 372 } 373 373 } -
trunk/src/org/openstreetmap/josm/data/osm/visitor/Visitor.java
r627 r1169 9 9 * Implementation of the visitor scheme. Every OsmPrimitive can be visited by 10 10 * several different visitors. 11 * 11 * 12 12 * @author imi 13 13 */ 14 14 public interface Visitor { 15 16 17 15 void visit(Node n); 16 void visit(Way w); 17 void visit(Relation e); 18 18 } -
trunk/src/org/openstreetmap/josm/data/projection/Epsg4326.java
r1032 r1169 12 12 public class Epsg4326 implements Projection { 13 13 14 15 16 14 public EastNorth latlon2eastNorth(LatLon p) { 15 return new EastNorth(p.lon(), p.lat()); 16 } 17 17 18 19 20 18 public LatLon eastNorth2latlon(EastNorth p) { 19 return new LatLon(p.north(), p.east()); 20 } 21 21 22 23 24 22 @Override public String toString() { 23 return tr("EPSG:4326"); 24 } 25 25 26 26 public String getCacheDirectoryName() { … … 28 28 } 29 29 30 31 30 public double scaleFactor() { 31 return 1.0/360; 32 32 } 33 33 34 35 36 34 @Override public boolean equals(Object o) { 35 return o instanceof Epsg4326; 36 } 37 37 38 39 40 38 @Override public int hashCode() { 39 return Epsg4326.class.hashCode(); 40 } 41 41 } -
trunk/src/org/openstreetmap/josm/data/projection/Lambert.java
r865 r1169 14 14 15 15 public class Lambert implements Projection { 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 16 /** 17 * Lambert I, II, III, and IV projection exponents 18 */ 19 public static final double n[] = { 0.7604059656, 0.7289686274, 0.6959127966, 0.6712679322 }; 20 21 /** 22 * Lambert I, II, III, and IV projection constants 23 */ 24 public static final double c[] = { 11603796.98, 11745793.39, 11947992.52, 12136281.99 }; 25 26 /** 27 * Lambert I, II, III, and IV false east 28 */ 29 public static final double Xs[] = { 600000.0, 600000.0, 600000.0, 234.358 }; 30 31 /** 32 * Lambert I, II, III, and IV false north 33 */ 34 public static final double Ys[] = { 5657616.674, 6199695.768, 6791905.085, 7239161.542 }; 35 36 /** 37 * Lambert I, II, III, and IV longitudinal offset to Greenwich meridian 38 */ 39 public static final double lg0 = 0.04079234433198; // 2deg20'14.025" 40 41 /** 42 * precision in iterative schema 43 */ 44 45 public static final double epsilon = 1e-11; 46 47 /** 48 * France is divided in 4 Lambert projection zones (1,2,3 + 4th for Corsica) 49 */ 50 public static final double cMaxLatZone1 = Math.toRadians(57 * 0.9); 51 52 public static final double zoneLimits[] = { Math.toRadians(53.5 * 0.9), // between Zone 1 and Zone 2 (in grad *0.9) 53 Math.toRadians(50.5 * 0.9), // between Zone 2 and Zone 3 54 Math.toRadians(47.51963 * 0.9), // between Zone 3 and Zone 4 55 Math.toRadians(46.17821 * 0.9) };// lowest latitude of Zone 4 56 57 public static final double cMinLonZones = Math.toRadians(-4.9074074074074059 * 0.9); 58 59 public static final double cMaxLonZones = Math.toRadians(10.2 * 0.9); 60 61 /** 62 * Because josm cannot work correctly if two zones are displayed, we allow some overlapping 63 */ 64 public static final double cMaxOverlappingZones = Math.toRadians(1.5 * 0.9); 65 66 public static int layoutZone = -1; 67 68 private static int currentZone = 0; 69 70 private static boolean dontDisplayErrors = false; 71 72 /** 73 * @param p WGS84 lat/lon (ellipsoid GRS80) (in degree) 74 * @return eastnorth projection in Lambert Zone (ellipsoid Clark) 75 */ 76 public EastNorth latlon2eastNorth(LatLon p) { 77 // translate ellipsoid GRS80 (WGS83) => Clark 78 LatLon geo = GRS802Clark(p); 79 double lt = geo.lat(); // in radian 80 double lg = geo.lon(); 81 82 // check if longitude and latitude are inside the french Lambert zones 83 currentZone = 0; 84 boolean outOfLambertZones = false; 85 if (lt >= zoneLimits[3] && lt <= cMaxLatZone1 && lg >= cMinLonZones && lg <= cMaxLonZones) { 86 // zone I 87 if (lt > zoneLimits[0]) 88 currentZone = 0; 89 // zone II 90 else if (lt > zoneLimits[1]) 91 currentZone = 1; 92 // zone III 93 else if (lt > zoneLimits[2]) 94 currentZone = 2; 95 // zone III or IV 96 else if (lt > zoneLimits[3]) 97 // Note: zone IV is dedicated to Corsica island and extends from 47.8 to 98 // 45.9 degrees of latitude. There is an overlap with zone III that can be 99 // solved only with longitude (covers Corsica if lon > 7.2 degree) 100 if (lg < Math.toRadians(8 * 0.9)) 101 currentZone = 2; 102 else 103 currentZone = 3; 104 } else { 105 outOfLambertZones = true; // possible when MAX_LAT is used 106 if (p.lat() != 0 && Math.abs(p.lat()) != Projection.MAX_LAT 107 && p.lon() != 0 && Math.abs(p.lon()) != Projection.MAX_LON 108 && dontDisplayErrors == false) { 109 JOptionPane.showMessageDialog(Main.parent, 110 tr("The projection \"{0}\" is designed for\n" 111 + "latitudes between 46.1° and 57° only.\n" 112 + "Use another projection system if you are not using\n" 113 + "a french WMS server.\n" 114 + "Do not upload any data after this message.", this.toString())); 115 dontDisplayErrors = true; 116 } 117 } 118 if (!outOfLambertZones) { 119 if (layoutZone == -1) { 120 layoutZone = currentZone; 121 dontDisplayErrors = false; 122 } else if (layoutZone != currentZone) { 123 if ((currentZone < layoutZone && Math.abs(zoneLimits[currentZone] - lt) > cMaxOverlappingZones) 124 || (currentZone > layoutZone && Math.abs(zoneLimits[layoutZone] - lt) > cMaxOverlappingZones)) { 125 JOptionPane.showMessageDialog(Main.parent, 126 tr("IMPORTANT : data positionned far away from\n" 127 + "the current Lambert zone limits.\n" 128 + "Do not upload any data after this message.\n" 129 + "Undo your last action, Save your work \n" 130 + "and Start a new layer on the new zone.")); 131 layoutZone = -1; 132 dontDisplayErrors = true; 133 } else { 134 System.out.println("temporarily extend Lambert zone " + layoutZone + " projection at lat,lon:" 135 + lt + "," + lg); 136 } 137 } 138 } 139 if (layoutZone == -1) { 140 return ConicProjection(lt, lg, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]); 141 } // else 142 return ConicProjection(lt, lg, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]); 143 } 144 145 public LatLon eastNorth2latlon(EastNorth p) { 146 LatLon geo; 147 if (layoutZone == -1) 148 // possible until the Lambert zone is determined by latlon2eastNorth() with a valid LatLon 149 geo = Geographic(p, Xs[currentZone], Ys[currentZone], c[currentZone], n[currentZone]); 150 else 151 geo = Geographic(p, Xs[layoutZone], Ys[layoutZone], c[layoutZone], n[layoutZone]); 152 // translate ellipsoid Clark => GRS80 (WGS83) 153 LatLon wgs = Clark2GRS80(geo); 154 return new LatLon(Math.toDegrees(wgs.lat()), Math.toDegrees(wgs.lon())); 155 } 156 157 @Override public String toString() { 158 return tr("Lambert Zone (France)"); 159 } 160 161 public String getCacheDirectoryName() { 162 return "lambert"; 163 } 164 165 public double scaleFactor() { 166 return 1.0 / 360; 167 } 168 169 @Override 170 public boolean equals(Object o) { 171 return o instanceof Lambert; 172 } 173 174 @Override 175 public int hashCode() { 176 return Lambert.class.hashCode(); 177 } 178 179 /** 180 * Initializes from geographic coordinates. Note that reference ellipsoid 181 * used by Lambert is the Clark ellipsoid. 182 * 183 * @param lat latitude in grad 184 * @param lon longitude in grad 185 * @param Xs false east (coordinate system origin) in meters 186 * @param Ys false north (coordinate system origin) in meters 187 * @param c projection constant 188 * @param n projection exponent 189 * @return EastNorth projected coordinates in meter 190 */ 191 private EastNorth ConicProjection(double lat, double lon, double Xs, double Ys, double c, double n) { 192 double eslt = Ellipsoid.clarke.e * Math.sin(lat); 193 double l = Math.log(Math.tan(Math.PI / 4.0 + (lat / 2.0)) 194 * Math.pow((1.0 - eslt) / (1.0 + eslt), Ellipsoid.clarke.e / 2.0)); 195 double east = Xs + c * Math.exp(-n * l) * Math.sin(n * (lon - lg0)); 196 double north = Ys - c * Math.exp(-n * l) * Math.cos(n * (lon - lg0)); 197 return new EastNorth(east, north); 198 } 199 200 /** 201 * Initializes from projected coordinates (conic projection). Note that 202 * reference ellipsoid used by Lambert is Clark 203 * 204 * @param coord projected coordinates pair in meters 205 * @param Xs false east (coordinate system origin) in meters 206 * @param Ys false north (coordinate system origin) in meters 207 * @param c projection constant 208 * @param n projection exponent 209 * @return LatLon in radian 210 */ 211 private LatLon Geographic(EastNorth eastNorth, double Xs, double Ys, double c, double n) { 212 double dx = eastNorth.east() - Xs; 213 double dy = Ys - eastNorth.north(); 214 double R = Math.sqrt(dx * dx + dy * dy); 215 double gamma = Math.atan(dx / dy); 216 double l = -1.0 / n * Math.log(Math.abs(R / c)); 217 l = Math.exp(l); 218 double lon = lg0 + gamma / n; 219 double lat = 2.0 * Math.atan(l) - Math.PI / 2.0; 220 double delta = 1.0; 221 while (delta > epsilon) { 222 double eslt = Ellipsoid.clarke.e * Math.sin(lat); 223 double nlt = 2.0 * Math.atan(Math.pow((1.0 + eslt) / (1.0 - eslt), Ellipsoid.clarke.e / 2.0) * l) - Math.PI 224 / 2.0; 225 delta = Math.abs(nlt - lat); 226 lat = nlt; 227 } 228 return new LatLon(lat, lon); // in radian 229 } 230 231 /** 232 * Translate latitude/longitude in WGS84, (ellipsoid GRS80) to Lambert 233 * geographic, (ellipsoid Clark) 234 * 235 * @param wgs 236 * @return 237 */ 238 private LatLon GRS802Clark(LatLon wgs) { 239 double lat = Math.toRadians(wgs.lat()); // degree to radian 240 double lon = Math.toRadians(wgs.lon()); 241 // WGS84 geographic => WGS84 cartesian 242 double N = Ellipsoid.GRS80.a / (Math.sqrt(1.0 - Ellipsoid.GRS80.e2 * Math.sin(lat) * Math.sin(lat))); 243 double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon); 244 double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon); 245 double Z = (N * (1.0 - Ellipsoid.GRS80.e2)/* + height */) * Math.sin(lat); 246 // WGS84 => Lambert ellipsoide similarity 247 X += 168.0; 248 Y += 60.0; 249 Z += -320.0; 250 // Lambert cartesian => Lambert geographic 251 return Geographic(X, Y, Z, Ellipsoid.clarke); 252 } 253 254 /** 255 * @param lambert 256 * @return 257 */ 258 private LatLon Clark2GRS80(LatLon lambert) { 259 double lat = lambert.lat(); // in radian 260 double lon = lambert.lon(); 261 // Lambert geographic => Lambert cartesian 262 double N = Ellipsoid.clarke.a / (Math.sqrt(1.0 - Ellipsoid.clarke.e2 * Math.sin(lat) * Math.sin(lat))); 263 double X = (N/* +height */) * Math.cos(lat) * Math.cos(lon); 264 double Y = (N/* +height */) * Math.cos(lat) * Math.sin(lon); 265 double Z = (N * (1.0 - Ellipsoid.clarke.e2)/* + height */) * Math.sin(lat); 266 // Lambert => WGS84 ellipsoide similarity 267 X += -168.0; 268 Y += -60.0; 269 Z += 320.0; 270 // WGS84 cartesian => WGS84 geographic 271 return Geographic(X, Y, Z, Ellipsoid.GRS80); 272 } 273 274 /** 275 * initializes from cartesian coordinates 276 * 277 * @param X 278 * 1st coordinate in meters 279 * @param Y 280 * 2nd coordinate in meters 281 * @param Z 282 * 3rd coordinate in meters 283 * @param ell 284 * reference ellipsoid 285 */ 286 private LatLon Geographic(double X, double Y, double Z, Ellipsoid ell) { 287 double norm = Math.sqrt(X * X + Y * Y); 288 double lg = 2.0 * Math.atan(Y / (X + norm)); 289 double lt = Math.atan(Z / (norm * (1.0 - (ell.a * ell.e2 / Math.sqrt(X * X + Y * Y + Z * Z))))); 290 double delta = 1.0; 291 while (delta > epsilon) { 292 double s2 = Math.sin(lt); 293 s2 *= s2; 294 double l = Math.atan((Z / norm) 295 / (1.0 - (ell.a * ell.e2 * Math.cos(lt) / (norm * Math.sqrt(1.0 - ell.e2 * s2))))); 296 delta = Math.abs(l - lt); 297 lt = l; 298 } 299 double s2 = Math.sin(lt); 300 s2 *= s2; 301 // h = norm / Math.cos(lt) - ell.a / Math.sqrt(1.0 - ell.e2 * s2); 302 return new LatLon(lt, lg); 303 } 304 304 305 305 } -
trunk/src/org/openstreetmap/josm/data/projection/Mercator.java
r1032 r1169 10 10 * Implement Mercator Projection code, coded after documentation 11 11 * from wikipedia. 12 * 13 * The center of the mercator projection is always the 0 grad 12 * 13 * The center of the mercator projection is always the 0 grad 14 14 * coordinate. 15 * 15 * 16 16 * @author imi 17 17 */ 18 18 public class Mercator implements Projection { 19 19 20 21 22 23 24 20 public EastNorth latlon2eastNorth(LatLon p) { 21 return new EastNorth( 22 p.lon()*Math.PI/180, 23 Math.log(Math.tan(Math.PI/4+p.lat()*Math.PI/360))); 24 } 25 25 26 27 28 29 30 26 public LatLon eastNorth2latlon(EastNorth p) { 27 return new LatLon( 28 Math.atan(Math.sinh(p.north()))*180/Math.PI, 29 p.east()*180/Math.PI); 30 } 31 31 32 33 34 32 @Override public String toString() { 33 return tr("Mercator"); 34 } 35 35 36 36 public String getCacheDirectoryName() { … … 38 38 } 39 39 40 41 40 public double scaleFactor() { 41 return 1/Math.PI/2; 42 42 } 43 43 44 45 46 44 @Override public boolean equals(Object o) { 45 return o instanceof Mercator; 46 } 47 47 48 49 50 48 @Override public int hashCode() { 49 return Mercator.class.hashCode(); 50 } 51 51 } -
trunk/src/org/openstreetmap/josm/data/projection/Projection.java
r656 r1169 6 6 7 7 /** 8 * Classes implementing this are able to convert lat/lon values to 8 * Classes implementing this are able to convert lat/lon values to 9 9 * planar screen coordinates. 10 * 10 * 11 11 * @author imi 12 12 */ 13 13 public interface Projection { 14 14 15 /** 16 * Maximum latitude representable. 17 */ 18 public static final double MAX_LAT = 85.05112877980659; // Mercator squares the world 19 20 /** 21 * Maximum longditude representable. 22 */ 23 public static final double MAX_LON = 180; 15 /** 16 * Maximum latitude representable. 17 */ 18 public static final double MAX_LAT = 85.05112877980659; // Mercator squares the world 24 19 25 26 * Minimum difference in location to not be represented as the same position.27 28 public static final double MAX_SERVER_PRECISION = 1e12;20 /** 21 * Maximum longditude representable. 22 */ 23 public static final double MAX_LON = 180; 29 24 30 /** 31 * List of all available projections. 32 */ 33 public static Projection[] allProjections = new Projection[]{ 34 new Epsg4326(), 35 new Mercator(), 36 new Lambert() 37 }; 38 39 /** 40 * Convert from lat/lon to northing/easting. 41 * 42 * @param p The geo point to convert. x/y members of the point are filled. 43 */ 44 EastNorth latlon2eastNorth(LatLon p); 45 46 /** 47 * Convert from norting/easting to lat/lon. 48 * 49 * @param p The geo point to convert. lat/lon members of the point are filled. 50 */ 51 LatLon eastNorth2latlon(EastNorth p); 25 /** 26 * Minimum difference in location to not be represented as the same position. 27 */ 28 public static final double MAX_SERVER_PRECISION = 1e12; 52 29 53 /** 54 * Describe the projection converter in one or two words. 55 */ 56 String toString(); 57 30 /** 31 * List of all available projections. 32 */ 33 public static Projection[] allProjections = new Projection[]{ 34 new Epsg4326(), 35 new Mercator(), 36 new Lambert() 37 }; 38 39 /** 40 * Convert from lat/lon to northing/easting. 41 * 42 * @param p The geo point to convert. x/y members of the point are filled. 43 */ 44 EastNorth latlon2eastNorth(LatLon p); 45 46 /** 47 * Convert from norting/easting to lat/lon. 48 * 49 * @param p The geo point to convert. lat/lon members of the point are filled. 50 */ 51 LatLon eastNorth2latlon(EastNorth p); 52 53 /** 54 * Describe the projection converter in one or two words. 55 */ 56 String toString(); 57 58 58 /** 59 59 * Get a filename compatible string (for the cache directory) 60 60 */ 61 61 String getCacheDirectoryName(); 62 62 63 63 /** 64 * The factor to multiply with an easting coordinate to get from "easting 64 * The factor to multiply with an easting coordinate to get from "easting 65 65 * units per pixel" to "meters per pixel" 66 66 */ -
trunk/src/org/openstreetmap/josm/gui/BookmarkList.java
r627 r1169 21 21 public class BookmarkList extends JList { 22 22 23 24 25 26 27 28 29 30 23 /** 24 * Create a bookmark list as well as the Buttons add and remove. 25 */ 26 public BookmarkList() { 27 setModel(new DefaultListModel()); 28 load(); 29 setVisibleRowCount(7); 30 } 31 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 32 /** 33 * Loads the bookmarks from file. 34 */ 35 public void load() { 36 DefaultListModel model = (DefaultListModel)getModel(); 37 model.removeAllElements(); 38 try { 39 for (Preferences.Bookmark b : Main.pref.loadBookmarks()) 40 model.addElement(b); 41 } catch (IOException e) { 42 e.printStackTrace(); 43 JOptionPane.showMessageDialog(Main.parent, tr("Could not read bookmarks.")+"\n"+e.getMessage()); 44 } 45 } 46 46 47 48 49 50 51 52 53 54 55 56 57 58 59 47 /** 48 * Save all bookmarks to the preferences file 49 */ 50 public void save() { 51 try { 52 Collection<Preferences.Bookmark> bookmarks = new LinkedList<Preferences.Bookmark>(); 53 for (Object o : ((DefaultListModel)getModel()).toArray()) 54 bookmarks.add((Preferences.Bookmark)o); 55 Main.pref.saveBookmarks(bookmarks); 56 } catch (IOException e) { 57 JOptionPane.showMessageDialog(Main.parent,tr("Could not write bookmark.")+"\n"+e.getMessage()); 58 } 59 } 60 60 } -
trunk/src/org/openstreetmap/josm/gui/ConflictResolver.java
r627 r1169 46 46 * three tables in the screen, one for each both sides and one resulting table. The user can 47 47 * move items from either one of the sides ("my" and "their") to the resulting table. 48 * 48 * 49 49 * @author Imi 50 50 */ 51 51 public class ConflictResolver extends JPanel { 52 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 53 public static enum Resolution {MY, THEIR} 54 55 private final class ConflictTableModel implements TableModel { 56 private final Resolution resolution; 57 public ConflictTableModel(Resolution resolution) { 58 this.resolution = resolution; 59 } 60 61 public int getRowCount() { 62 return conflicts.size(); 63 } 64 65 public Object getValueAt(int rowIndex, int columnIndex) { 66 ConflictItem ci = conflicts.get(rowIndex); 67 if (columnIndex == 0) 68 return ci.key(); 69 Resolution r = resolution == null ? ci.resolution : resolution; 70 if (r == null) 71 return "<html><i>???</i></html>"; 72 JLabel l = new JLabel(r == Resolution.MY ? ci.my : ci.their); 73 if (ci.resolution == resolution && resolution != null) 74 l.setFont(l.getFont().deriveFont(Font.BOLD)); 75 return l; 76 } 77 78 public String getColumnName(int columnIndex) {return columnIndex == 0 ? tr("Key") : tr("Value");} 79 public int getColumnCount() {return 2;} 80 public boolean isCellEditable(int row, int column) {return false;} 81 public Class<?> getColumnClass(int columnIndex) {return Object.class;} 82 83 public void addTableModelListener(TableModelListener l) {} 84 public void removeTableModelListener(TableModelListener l) {} 85 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {} 86 } 87 88 private final class DblClickListener extends MouseAdapter { 89 private final Resolution resolution; 90 public DblClickListener(Resolution resolution) { 91 this.resolution = resolution; 92 } 93 @Override public void mouseClicked(MouseEvent e) { 94 if (e.getClickCount() >= 2) { 95 int sel = ((JTable)e.getSource()).getSelectedRow(); 96 if (sel == -1) 97 return; 98 ConflictResolver.this.conflicts.get(sel).resolution = resolution; 99 repaint(); 100 } 101 } 102 } 103 private final class ResolveAction extends AbstractAction { 104 private final Resolution resolution; 105 public ResolveAction(String name, Resolution resolution) { 106 super(null, ImageProvider.get("dialogs", name)); 107 this.resolution = resolution; 108 } 109 public void actionPerformed(ActionEvent e) { 110 int sel = myTable.getSelectedRow(); 111 if (sel == -1) 112 return; 113 conflicts.get(sel).resolution = resolution; 114 if (sel == myTable.getRowCount()-1) 115 myTable.clearSelection(); 116 else 117 myTable.getSelectionModel().setSelectionInterval(sel+1, sel+1); 118 repaint(); 119 } 120 } 121 122 public final List<ConflictItem> conflicts = new ArrayList<ConflictItem>(); 123 124 private final ConflictTableModel my = new ConflictTableModel(Resolution.MY); 125 private final JTable myTable; 126 private final ConflictTableModel their = new ConflictTableModel(Resolution.THEIR); 127 private final JTable theirTable; 128 private final ConflictTableModel resolve = new ConflictTableModel(null); 129 private final JTable resolveTable; 130 131 132 public ConflictResolver(Map<OsmPrimitive, OsmPrimitive> conflicts) { 133 super(new GridBagLayout()); 134 Collection<ConflictItem> possibleConflicts = new ArrayList<ConflictItem>(); 135 possibleConflicts.add(new DeleteConflict()); 136 possibleConflicts.add(new PositionConflict()); 137 TreeSet<String> allkeys = new TreeSet<String>(); 138 for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) { 139 allkeys.addAll(e.getKey().keySet()); 140 allkeys.addAll(e.getValue().keySet()); 141 } 142 for (String s : allkeys) 143 possibleConflicts.add(new PropertyConflict(s)); 144 145 for (Entry<OsmPrimitive, OsmPrimitive> e : conflicts.entrySet()) { 146 for (Iterator<ConflictItem> it = possibleConflicts.iterator(); it.hasNext();) { 147 ConflictItem ci = it.next(); 148 if (ci.hasConflict(e.getKey(), e.getValue())) { 149 ci.initialize(conflicts); 150 this.conflicts.add(ci); 151 it.remove(); 152 } 153 } 154 } 155 156 157 // have to initialize the JTables here and not in the declaration, because its constructor 158 // may access this.conflicts (indirectly) 159 myTable = new JTable(my); 160 theirTable = new JTable(their); 161 resolveTable = new JTable(resolve); 162 163 myTable.setPreferredScrollableViewportSize(new Dimension(250,70)); 164 theirTable.setPreferredScrollableViewportSize(new Dimension(250,70)); 165 resolveTable.setPreferredScrollableViewportSize(new Dimension(250,70)); 166 167 TableCellRenderer renderer = new DefaultTableCellRenderer(){ 168 final Font defFont = new JLabel().getFont(); 169 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 170 JLabel c = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 171 c.setIcon(null); 172 c.setFont(defFont); 173 if (value instanceof JLabel) { 174 JLabel l = (JLabel)value; 175 String text = l.getText(); 176 c.setText(text); 177 c.setFont(l.getFont()); 178 if (text.startsWith("<html>") && l.getFont().isBold()) 179 c.setText("<html>"+"<b>"+text.substring(6, text.length()-12)); 180 } else { 181 String s = value.toString(); 182 int i = s.indexOf('|'); 183 if (i != -1) { 184 c.setIcon(ImageProvider.get("data", s.substring(0,i))); 185 c.setText(s.substring(i+1)); 186 } 187 } 188 return c; 189 } 190 }; 191 myTable.setDefaultRenderer(Object.class, renderer); 192 theirTable.setDefaultRenderer(Object.class, renderer); 193 resolveTable.setDefaultRenderer(Object.class, renderer); 194 195 myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 196 theirTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 197 resolveTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 198 ListSelectionListener selListener = new ListSelectionListener(){ 199 public void valueChanged(ListSelectionEvent e) { 200 if (((ListSelectionModel)e.getSource()).isSelectionEmpty()) { 201 myTable.clearSelection(); 202 theirTable.clearSelection(); 203 resolveTable.clearSelection(); 204 } else { 205 int i = ((ListSelectionModel)e.getSource()).getMinSelectionIndex(); 206 myTable.scrollRectToVisible(myTable.getCellRect(i, 0, true)); 207 myTable.getSelectionModel().setSelectionInterval(i, i); 208 theirTable.scrollRectToVisible(theirTable.getCellRect(i, 0, true)); 209 theirTable.getSelectionModel().setSelectionInterval(i, i); 210 resolveTable.scrollRectToVisible(resolveTable.getCellRect(i, 0, true)); 211 resolveTable.getSelectionModel().setSelectionInterval(i, i); 212 } 213 } 214 }; 215 myTable.getSelectionModel().addListSelectionListener(selListener); 216 theirTable.getSelectionModel().addListSelectionListener(selListener); 217 resolveTable.getSelectionModel().addListSelectionListener(selListener); 218 myTable.getSelectionModel().setSelectionInterval(0,0); 219 220 myTable.addMouseListener(new DblClickListener(Resolution.MY)); 221 theirTable.addMouseListener(new DblClickListener(Resolution.THEIR)); 222 resolveTable.addMouseListener(new DblClickListener(null)); 223 224 add(new JLabel(trn("{0} object has conflicts:","{0} objects have conflicts:",conflicts.size(),conflicts.size())), GBC.eol().insets(0,0,0,10)); 225 226 JPanel p = new JPanel(new GridBagLayout()); 227 p.add(new JLabel(tr("my version:")), GBC.eol()); 228 p.add(new JScrollPane(myTable), GBC.eol().fill(GBC.BOTH)); 229 p.add(new JButton(new ResolveAction("down", Resolution.MY)), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,0)); 230 add(p, GBC.std().insets(0,0,5,0)); 231 232 p = new JPanel(new GridBagLayout()); 233 p.add(new JLabel(tr("their version:")), GBC.eol()); 234 p.add(new JScrollPane(theirTable), GBC.eol().fill(GBC.BOTH)); 235 p.add(new JButton(new ResolveAction("down", Resolution.THEIR)), GBC.eol().anchor(GBC.CENTER).insets(0,5,0,0)); 236 add(p, GBC.eop().insets(5,0,0,0)); 237 238 add(new JButton(new ResolveAction("up", null)), GBC.eol().anchor(GBC.CENTER)); 239 add(new JLabel(tr("resolved version:")), GBC.eol().insets(0,5,0,0)); 240 add(new JScrollPane(resolveTable), GBC.eol().anchor(GBC.CENTER).fill(GBC.BOTH)); 241 } 242 242 } -
trunk/src/org/openstreetmap/josm/gui/GettingStarted.java
r1163 r1169 25 25 public class GettingStarted extends JPanel { 26 26 27 static private String content = ""; 27 static private String content = ""; 28 28 29 29 public class LinkGeneral extends JEditorPane implements HyperlinkListener { … … 74 74 boolean included = false; 75 75 if (condition.equals("%3E")) { 76 if ((myVersion == 0 || myVersion > targetVersion) 76 if ((myVersion == 0 || myVersion > targetVersion) 77 77 /* && ! Main.pref.getBoolean("motd.gt."+targetVersion) */) { 78 78 /* Main.pref.put("motd.gt."+targetVersion, true); */ … … 80 80 } 81 81 } else if (condition.equals("%3E%3D")) { 82 if ((myVersion == 0 || myVersion >= targetVersion) 82 if ((myVersion == 0 || myVersion >= targetVersion) 83 83 /* && ! Main.pref.getBoolean("motd.ge."+targetVersion) */) { 84 84 /* Main.pref.put("motd.ge."+targetVersion, true); */ … … 114 114 content += wr.read(url).replace("<html>", "").replace("</html>", "").replace("<div id=\"searchable\">", "").replace("</div>", ""); 115 115 } catch (IOException ioe2) { 116 } 117 } 116 } 117 } 118 118 } 119 119 } 120 120 content += motdcontent.substring(start); 121 121 content = content.replace("<html>", "<html><style type=\"text/css\">\nbody { font-family: sans-serif; font-weight: bold; }\nh1 {text-align: center;}</style>"); 122 /* replace the wiki title */ 123 content = content.replace("JOSM, the Java OpenStreetMap editor", tr("JOSM, the Java OpenStreetMap editor")); 122 /* replace the wiki title */ 123 content = content.replace("JOSM, the Java OpenStreetMap editor", tr("JOSM, the Java OpenStreetMap editor")); 124 124 } 125 125 126 126 } 127 127 128 128 public GettingStarted() { 129 129 super(new BorderLayout()); 130 130 assignContent(); 131 131 132 132 // panel.add(GBC.glue(0,1), GBC.eol()); 133 133 //panel.setMinimumSize(new Dimension(400, 600)); -
trunk/src/org/openstreetmap/josm/gui/IconToggleButton.java
r627 r1169 18 18 public class IconToggleButton extends JToggleButton implements PropertyChangeListener { 19 19 20 20 public boolean groupbutton; 21 21 22 23 24 25 26 27 22 /** 23 * Construct the toggle button with the given action. 24 */ 25 public IconToggleButton(Action action) { 26 super(action); 27 setText(null); 28 28 29 30 31 29 Object o = action.getValue(Action.SHORT_DESCRIPTION); 30 if (o != null) 31 setToolTipText(o.toString()); 32 32 33 34 35 36 37 33 action.addPropertyChangeListener(this); 34 35 addMouseListener(new MouseAdapter(){ 36 @Override public void mousePressed(MouseEvent e) { 37 groupbutton = e.getX() > getWidth()/2 && e.getY() > getHeight()/2; 38 38 } 39 40 39 }); 40 } 41 41 42 43 44 45 46 47 42 public void propertyChange(PropertyChangeEvent evt) { 43 if (evt.getPropertyName().equals("active")) { 44 setSelected((Boolean)evt.getNewValue()); 45 requestFocusInWindow(); 46 } 47 } 48 48 } -
trunk/src/org/openstreetmap/josm/gui/JMultilineLabel.java
r729 r1169 1 1 // License: GPL. For details, see LICENSE file. 2 2 3 // This class was taken from 3 // This class was taken from 4 4 // http://forum.java.sun.com/thread.jspa?threadID=459705&messageID=2104021 5 5 // - Removed hardcoded margin … … 22 22 23 23 public class JMultilineLabel extends JComponent { 24 25 26 27 24 private String text; 25 private int maxWidth = Integer.MAX_VALUE; 26 private boolean justify; 27 private final FontRenderContext frc = new FontRenderContext(null, false, false); 28 28 29 30 31 32 29 public JMultilineLabel(String description) { 30 super(); 31 setText(description); 32 } 33 33 34 35 36 37 34 private void morph() { 35 revalidate(); 36 repaint(); 37 } 38 38 39 40 41 39 public String getText() { 40 return text; 41 } 42 42 43 44 45 46 47 48 49 43 public void setText(String text) { 44 String old = this.text; 45 this.text = text; 46 firePropertyChange("text", old, this.text); 47 if ((old == null) ? text!=null : !old.equals(text)) 48 morph(); 49 } 50 50 51 52 53 51 public int getMaxWidth() { 52 return maxWidth; 53 } 54 54 55 56 57 58 59 60 61 if (old !=this.maxWidth)62 63 55 public void setMaxWidth(int maxWidth) { 56 if (maxWidth <= 0) 57 throw new IllegalArgumentException(); 58 int old = this.maxWidth; 59 this.maxWidth = maxWidth; 60 firePropertyChange("maxWidth", old, this.maxWidth); 61 if (old != this.maxWidth) 62 morph(); 63 } 64 64 65 66 67 65 public boolean isJustified() { 66 return justify; 67 } 68 68 69 70 71 72 73 74 75 69 public void setJustified(boolean justify) { 70 boolean old = this.justify; 71 this.justify = justify; 72 firePropertyChange("justified", old, this.justify); 73 if (old != this.justify) 74 repaint(); 75 } 76 76 77 78 79 77 public Dimension getPreferredSize() { 78 return paintOrGetSize(null, getMaxWidth()); 79 } 80 80 81 82 83 81 public Dimension getMinimumSize() { 82 return getPreferredSize(); 83 } 84 84 85 86 87 88 85 protected void paintComponent(Graphics g) { 86 super.paintComponent(g); 87 paintOrGetSize((Graphics2D)g, getWidth()); 88 } 89 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 90 private Dimension paintOrGetSize(Graphics2D g, int width) { 91 Insets insets = getInsets(); 92 width -= insets.left + insets.right; 93 float w = insets.left + insets.right; 94 float x = insets.left, y=insets.top; 95 if (width > 0 && text != null && text.length() > 0) { 96 AttributedString as = new AttributedString(getText()); 97 as.addAttribute(TextAttribute.FONT, getFont()); 98 AttributedCharacterIterator aci = as.getIterator(); 99 LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); 100 float max = 0; 101 while (lbm.getPosition() < aci.getEndIndex()) { 102 TextLayout textLayout = lbm.nextLayout(width); 103 if (g != null && isJustified() && textLayout.getVisibleAdvance() > 0.80 * width) 104 textLayout = textLayout.getJustifiedLayout(width); 105 if (g != null) 106 textLayout.draw(g, x, y + textLayout.getAscent()); 107 y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent(); 108 max = Math.max(max, textLayout.getVisibleAdvance()); 109 } 110 w += max; 111 } 112 return new Dimension((int)Math.ceil(w), (int)Math.ceil(y) + insets.bottom); 113 } 114 114 } -
trunk/src/org/openstreetmap/josm/gui/MainApplet.java
r1146 r1169 33 33 public class MainApplet extends JApplet { 34 34 35 36 37 38 35 public static final class UploadPreferencesAction extends JosmAction { 36 public UploadPreferencesAction() { 37 super(tr("Upload Preferences"), "upload-preferences", tr("Upload the current preferences to the server"), 38 Shortcut.registerShortcut("applet:uploadprefs", tr("Upload Preferences"), KeyEvent.VK_U, Shortcut.GROUP_HOTKEY), true); 39 39 } 40 41 42 40 public void actionPerformed(ActionEvent e) { 41 ((ServerSidePreferences)Main.pref).upload(); 42 } 43 43 } 44 44 45 45 private final class MainCaller extends Main { 46 47 48 49 50 51 46 private MainCaller() { 47 setContentPane(contentPane); 48 setJMenuBar(menu); 49 setBounds(bounds); 50 } 51 } 52 52 53 54 55 56 57 58 59 60 61 53 private final static String[][] paramInfo = { 54 {"username", tr("string"), tr("Name of the user.")}, 55 {"password", tr("string"), tr("OSM Password.")}, 56 {"geometry", tr("string"), tr("Resize the applet to the given geometry (format: WIDTHxHEIGHT)")}, 57 {"download", tr("string;string;..."), tr("Download each. Can be x1,y1,x2,y2 an url containing lat=y&lon=x&zoom=z or a filename")}, 58 {"downloadgps", tr("string;string;..."), tr("Download each as raw gps. Can be x1,y1,x2,y2 an url containing lat=y&lon=x&zoom=z or a filename")}, 59 {"selection", tr("string;string;..."), tr("Add each to the initial selection. Can be a google-like search string or an url which returns osm-xml")}, 60 {"reset-preferences", tr("any"),tr("If specified, reset the configuration instead of reading it.")} 61 }; 62 62 63 63 private Map<String, Collection<String>> args = new HashMap<String, Collection<String>>(); 64 64 65 66 67 65 @Override public String[][] getParameterInfo() { 66 return paramInfo; 67 } 68 68 69 70 71 72 73 74 75 76 77 78 69 @Override public void init() { 70 for (String[] s : paramInfo) { 71 Collection<String> p = readParameter(s[0], args.get(s[0])); 72 if (p != null) 73 args.put(s[0], p); 74 } 75 if (!args.containsKey("geometry") && getParameter("width") != null && getParameter("height") != null) { 76 args.put("geometry", Arrays.asList(new String[]{getParameter("width")+"x"+getParameter("height")})); 77 } 78 } 79 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 80 @Override public void start() { 81 String username = args.containsKey("username") ? args.get("username").iterator().next() : null; 82 String password = args.containsKey("password") ? args.get("password").iterator().next() : null; 83 if (username == null || password == null) { 84 JPanel p = new JPanel(new GridBagLayout()); 85 p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,20,0)); 86 JTextField user = new JTextField(username == null ? "" : username); 87 p.add(user, GBC.eol().fill(GBC.HORIZONTAL)); 88 p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,20,0)); 89 JPasswordField pass = new JPasswordField(password == null ? "" : password); 90 p.add(pass, GBC.eol().fill(GBC.HORIZONTAL)); 91 JOptionPane.showMessageDialog(null, p); 92 username = user.getText(); 93 password = new String(pass.getPassword()); 94 args.put("password", Arrays.asList(new String[]{password})); 95 } 96 96 97 98 99 97 Main.applet = true; 98 Main.pref = new ServerSidePreferences(getCodeBase()); 99 ((ServerSidePreferences)Main.pref).download(username, password); 100 100 101 102 103 101 Main.preConstructorInit(args); 102 Main.parent = this; 103 new MainCaller().postConstructorProcessCmdLine(args); 104 104 105 105 MainMenu m = Main.main.menu; // shortcut 106 106 107 108 109 110 111 112 113 114 115 107 // remove offending stuff from JOSM (that would break the SecurityManager) 108 m.remove(m.fileMenu); 109 m.editMenu.add(new UploadPreferencesAction()); 110 m.openFile.setEnabled(false); 111 m.exit.setEnabled(false); 112 m.save.setEnabled(false); 113 m.saveAs.setEnabled(false); 114 m.gpxExport.setEnabled(false); 115 } 116 116 117 118 119 120 121 122 123 124 125 117 private Collection<String> readParameter(String s, Collection<String> v) { 118 String param = getParameter(s); 119 if (param != null) { 120 if (v == null) 121 v = new LinkedList<String>(); 122 v.addAll(Arrays.asList(param.split(";"))); 123 } 124 return v; 125 } 126 126 127 128 129 130 131 132 133 127 public static void main(String[] args) { 128 final JFrame frame = new JFrame("Java OpenStreetMap Applet"); 129 MainApplet applet = new MainApplet(); 130 applet.setStub(new AppletStub() { 131 public void appletResize(int w, int h) { 132 frame.setSize(w, h); 133 } 134 134 135 136 137 135 public AppletContext getAppletContext() { 136 return null; 137 } 138 138 139 140 141 142 143 144 145 146 139 public URL getCodeBase() { 140 try { 141 return new File(".").toURI().toURL(); 142 } catch (Exception e) { 143 e.printStackTrace(); 144 return null; 145 } 146 } 147 147 148 149 150 148 public URL getDocumentBase() { 149 return getCodeBase(); 150 } 151 151 152 153 154 152 public String getParameter(String k) { 153 return null; 154 } 155 155 156 157 158 159 160 161 162 163 164 156 public boolean isActive() { 157 return true; 158 } 159 }); 160 applet.init(); 161 applet.start(); 162 frame.setContentPane(applet); 163 frame.setVisible(true); 164 } 165 165 } -
trunk/src/org/openstreetmap/josm/gui/MainApplication.java
r1082 r1169 37 37 */ 38 38 public class MainApplication extends Main { 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 39 /** 40 * Allow subclassing (see JOSM.java) 41 */ 42 public MainApplication() {} 43 44 /** 45 * Construct an main frame, ready sized and operating. Does not 46 * display the frame. 47 */ 48 public MainApplication(JFrame mainFrame) { 49 super(); 50 mainFrame.setContentPane(contentPane); 51 mainFrame.setJMenuBar(menu); 52 mainFrame.setBounds(bounds); 53 mainFrame.setIconImage(ImageProvider.get("logo.png").getImage()); 54 mainFrame.addWindowListener(new WindowAdapter(){ 55 @Override public void windowClosing(final WindowEvent arg0) { 56 if (Main.breakBecauseUnsavedChanges()) 57 return; 58 System.exit(0); 59 } 60 }); 61 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 62 } 63 64 /** 65 * Main application Startup 66 */ 67 public static void main(final String[] argArray) { 68 ///////////////////////////////////////////////////////////////////////// 69 // TO ALL TRANSLATORS 70 ///////////////////////////////////////////////////////////////////////// 71 // Do not translate the early strings below until the locale is set up. 72 // (By the eager loaded plugins) 73 // 74 // These strings cannot be translated. That's life. Really. Sorry. 75 // 76 // Imi. 77 ///////////////////////////////////////////////////////////////////////// 78 79 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler()); 80 81 // initialize the plaform hook, and 82 Main.determinePlatformHook(); 83 // call the really early hook before we anything else 84 Main.platform.preStartupHook(); 85 86 // construct argument table 87 List<String> argList = Arrays.asList(argArray); 88 final Map<String, Collection<String>> args = new HashMap<String, Collection<String>>(); 89 for (String arg : argArray) { 90 if (!arg.startsWith("--")) 91 arg = "--download="+arg; 92 int i = arg.indexOf('='); 93 String key = i == -1 ? arg.substring(2) : arg.substring(2,i); 94 String value = i == -1 ? "" : arg.substring(i+1); 95 Collection<String> v = args.get(key); 96 if (v == null) 97 v = new LinkedList<String>(); 98 v.add(value); 99 args.put(key, v); 100 } 101 102 if (argList.contains("--help") || argList.contains("-?") || argList.contains("-h")) { 103 // TODO: put in a platformHook for system that have no console by default 104 System.out.println(tr("Java OpenStreetMap Editor")+"\n\n"+ 105 tr("usage")+":\n"+ 106 "\tjava -jar josm.jar <option> <option> <option>...\n\n"+ 107 tr("options")+":\n"+ 108 "\t--help|-?|-h "+tr("Show this help")+"\n"+ 109 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+"\n"+ 110 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+"\n"+ 111 "\t[--download=]<url> "+tr("Download the location at the url (with lat=x&lon=y&zoom=z)")+"\n"+ 112 "\t[--download=]<filename> "+tr("Open file (as raw gps, if .gpx)")+"\n"+ 113 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw gps")+"\n"+ 114 "\t--selection=<searchstring> "+tr("Select with the given search")+"\n"+ 115 "\t--no-fullscreen "+tr("Don't launch in fullscreen mode")+"\n"+ 116 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+ 117 "\t--language=<language> "+tr("Set the language.")+"\n\n"+ 118 tr("examples")+":\n"+ 119 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+ 120 "\tjava -jar josm.jar http://www.openstreetmap.org/index.html?lat=43.2&lon=11.1&zoom=13\n"+ 121 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+ 122 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n\n"+ 123 124 tr("Parameters are read in the order they are specified, so make sure you load\n"+ 125 "some data before --selection")+"\n\n"+ 126 tr("Instead of --download=<bbox> you may specify osm://<bbox>\n")); 127 System.exit(0); 128 } 129 130 // get the preferences. 131 final File prefDir = new File(Main.pref.getPreferencesDir()); 132 // check if preferences directory has moved (TODO: Update code. Remove this after some time) 133 File oldPrefDir = new File(System.getProperty("user.home"), ".josm"); 134 if (!prefDir.isDirectory() && oldPrefDir.isDirectory()) { 135 if (oldPrefDir.renameTo(prefDir)) { 136 // do not translate this 137 JOptionPane.showMessageDialog(null, "The preference directory has been moved to "+prefDir); 138 } else { 139 JOptionPane.showMessageDialog(null, "The preference directory location has changed. Please move "+oldPrefDir+" to "+prefDir); 140 } 141 } 142 143 if (prefDir.exists() && !prefDir.isDirectory()) { 144 JOptionPane.showMessageDialog(null, "Cannot open preferences directory: "+Main.pref.getPreferencesDir()); 145 return; 146 } 147 if (!prefDir.exists()) 148 prefDir.mkdirs(); 149 150 if (!new File(Main.pref.getPreferencesDir()+"preferences").exists()) { 151 Main.pref.resetToDefault(); 152 } 153 154 try { 155 if (args.containsKey("reset-preferences")) { 156 Main.pref.resetToDefault(); 157 } else { 158 Main.pref.load(); 159 } 160 } catch (final IOException e1) { 161 e1.printStackTrace(); 162 String backup = Main.pref.getPreferencesDir() + "preferences.bak"; 163 JOptionPane.showMessageDialog(null, "Preferences file had errors. Making backup of old one to " + backup); 164 new File(Main.pref.getPreferencesDir() + "preferences").renameTo(new File(backup)); 165 Main.pref.save(); 166 } 167 167 168 168 // TODO remove this in early 2009 - just here to weed out color setting we don't use any more 169 169 Main.pref.put("downloaded Area", null); 170 170 171 172 173 174 if(args.containsKey("language")) 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 171 String localeName = null; //The locale to use 172 173 //Check if passed as parameter 174 if(args.containsKey("language")) 175 localeName = (String)(args.get("language").toArray()[0]); 176 177 if (localeName == null) { 178 localeName = Main.pref.get("language", null); 179 } 180 181 if (localeName != null) { 182 Locale l; 183 int i = localeName.indexOf('_'); 184 if (i > 0) { 185 l = new Locale(localeName.substring(0, i), localeName.substring(i + 1)); 186 } else { 187 l = new Locale(localeName); 188 } 189 Locale.setDefault(l); 190 } 191 191 try { 192 192 i18n = I18nFactory.getI18n(MainApplication.class); … … 194 194 System.out.println("Unable to find translation for the locale: " + Locale.getDefault().getDisplayName() + " reverting to English."); 195 195 } 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 196 197 SplashScreen splash = new SplashScreen(Main.pref.getBoolean("draw.splashscreen", true)); 198 199 splash.setStatus(tr("Activating updated plugins")); 200 if (!PluginDownloader.moveUpdatedPlugins()) { 201 JOptionPane.showMessageDialog(null, 202 tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."), 203 tr("Plugins"), JOptionPane.ERROR_MESSAGE); 204 } 205 206 // load the early plugins 207 splash.setStatus(tr("Loading early plugins")); 208 Main.loadPlugins(true); 209 210 splash.setStatus(tr("Setting defaults")); 211 preConstructorInit(args); 212 splash.setStatus(tr("Creating main GUI")); 213 JFrame mainFrame = new JFrame(tr("Java OpenStreetMap - Editor")); 214 Main.parent = mainFrame; 215 final Main main = new MainApplication(mainFrame); 216 splash.setStatus(tr("Loading plugins")); 217 Main.loadPlugins(false); 218 toolbar.refreshToolbarControl(); 219 220 mainFrame.setVisible(true); 221 splash.closeSplash(); 222 223 if (!args.containsKey("no-fullscreen") && !args.containsKey("geometry") && Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) 224 mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH); 225 226 EventQueue.invokeLater(new Runnable(){ 227 public void run() { 228 main.postConstructorProcessCmdLine(args); 229 } 230 }); 231 } 232 232 233 233 } -
trunk/src/org/openstreetmap/josm/gui/MainMenu.java
r1161 r1169 68 68 * This is the JOSM main menu bar. It is overwritten to initialize itself and provide all menu 69 69 * entries as member variables (sort of collect them). 70 * 70 * 71 71 * It also provides possibilities to attach new menu entries (used by plugins). 72 * 72 * 73 73 * @author Immanuel.Scholz 74 74 */ … … 140 140 /** 141 141 * Add a JosmAction to a menu. 142 * 142 * 143 143 * This method handles all the shortcut handling. It also makes sure that actions that are 144 144 * handled by the OS are not duplicated on the menu. … … 156 156 /** 157 157 * Add a menu to the main menu. 158 * 158 * 159 159 * This method handles all the shortcut handling. 160 160 */ -
trunk/src/org/openstreetmap/josm/gui/MapFrame.java
r1153 r1169 35 35 * One Map frame with one dataset behind. This is the container gui class whose 36 36 * display can be set to the different views. 37 * 37 * 38 38 * @author imi 39 39 */ 40 40 public class MapFrame extends JPanel implements Destroyable { 41 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 } 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 * @param mapModeThe new mode to set.176 177 178 179 180 181 182 183 184 185 186 187 188 189 * 190 191 192 193 194 195 196 197 198 199 200 201 202 42 /** 43 * The current mode, this frame operates. 44 */ 45 public MapMode mapMode; 46 /** 47 * The view control displayed. 48 */ 49 public MapView mapView; 50 /** 51 * The toolbar with the action icons. To add new toggle dialog actions, use addToggleDialog 52 * instead of adding directly to this list. To add a new mode use addMapMode. 53 */ 54 public JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL); 55 public JToolBar toolBarToggle = new JToolBar(JToolBar.VERTICAL); 56 /** 57 * The status line below the map 58 */ 59 public MapStatus statusLine; 60 61 public ConflictDialog conflictDialog; 62 /** 63 * The dialog that shows all relations and lets the user edit them. 64 */ 65 public RelationListDialog relationListDialog; 66 /** 67 * The panel list of all toggle dialog icons. To add new toggle dialog actions, use addToggleDialog 68 * instead of adding directly to this list. 69 */ 70 public JPanel toggleDialogs = new JPanel(); 71 72 public final ButtonGroup toolGroup = new ButtonGroup(); 73 74 public MapFrame() { 75 setSize(400,400); 76 setLayout(new BorderLayout()); 77 78 add(mapView = new MapView(), BorderLayout.CENTER); 79 80 // show menu entry 81 Main.main.menu.viewMenu.setVisible(true); 82 83 // toolbar 84 toolBarActions.setFloatable(false); 85 addMapMode(new IconToggleButton(new ZoomAction(this))); 86 addMapMode(new IconToggleButton(new SelectAction(this))); 87 addMapMode(new IconToggleButton(new DrawAction(this))); 88 addMapMode(new IconToggleButton(new DeleteAction(this))); 89 addMapMode(new IconToggleButton(new ExtrudeAction(this))); 90 91 toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true); 92 93 add(toggleDialogs, BorderLayout.EAST); 94 toggleDialogs.setLayout(new BoxLayout(toggleDialogs, BoxLayout.Y_AXIS)); 95 96 toolBarToggle.setFloatable(false); 97 addToggleDialog(new LayerListDialog(this)); 98 addToggleDialog(new PropertiesDialog(this)); 99 addToggleDialog(new HistoryDialog()); 100 addToggleDialog(new SelectionListDialog()); 101 addToggleDialog(new UserListDialog()); 102 addToggleDialog(conflictDialog = new ConflictDialog()); 103 addToggleDialog(new CommandStackDialog(this)); 104 addToggleDialog(relationListDialog = new RelationListDialog()); 105 106 // status line below the map 107 statusLine = new MapStatus(this); 108 } 109 110 /** 111 * Called as some kind of destructor when the last layer has been removed. 112 * Delegates the call to all Destroyables within this component (e.g. MapModes) 113 */ 114 public void destroy() { 115 for (int i = 0; i < toolBarActions.getComponentCount(); ++i) 116 if (toolBarActions.getComponent(i) instanceof Destroyable) 117 ((Destroyable)toolBarActions).destroy(); 118 for (int i = 0; i < toolBarToggle.getComponentCount(); ++i) 119 if (toolBarToggle.getComponent(i) instanceof Destroyable) 120 ((Destroyable)toolBarToggle).destroy(); 121 122 // remove menu entries 123 Main.main.menu.viewMenu.setVisible(false); 124 } 125 126 public Action getDefaultButtonAction() { 127 return ((AbstractButton)toolBarActions.getComponent(0)).getAction(); 128 } 129 130 /** 131 * Open all ToggleDialogs that have their preferences property set. Close all others. 132 */ 133 public void setVisibleDialogs() { 134 for (Component c : toggleDialogs.getComponents()) { 135 if (c instanceof ToggleDialog) { 136 boolean sel = Main.pref.getBoolean(((ToggleDialog)c).prefName+".visible"); 137 ((ToggleDialog)c).action.button.setSelected(sel); 138 c.setVisible(sel); 139 } 140 } 141 } 142 143 /** 144 * Call this to add new toggle dialogs to the left button-list 145 * @param dlg The toggle dialog. It must not be in the list already. 146 */ 147 public void addToggleDialog(ToggleDialog dlg) { 148 IconToggleButton button = new IconToggleButton(dlg.action); 149 dlg.action.button = button; 150 dlg.parent = toggleDialogs; 151 toolBarToggle.add(button); 152 toggleDialogs.add(dlg); 153 } 154 155 public void addMapMode(IconToggleButton b) { 156 toolBarActions.add(b); 157 toolGroup.add((AbstractButton)b); 158 } 159 160 /** 161 * Fires an property changed event "visible". 162 */ 163 @Override public void setVisible(boolean aFlag) { 164 boolean old = isVisible(); 165 super.setVisible(aFlag); 166 if (old != aFlag) 167 firePropertyChange("visible", old, aFlag); 168 } 169 170 171 172 /** 173 * Change the operating map mode for the view. Will call unregister on the 174 * old MapMode and register on the new one. 175 * @param mapMode The new mode to set. 176 */ 177 public void selectMapMode(MapMode mapMode) { 178 if (mapMode == this.mapMode) 179 return; 180 if (this.mapMode != null) 181 this.mapMode.exitMode(); 182 this.mapMode = mapMode; 183 mapMode.enterMode(); 184 } 185 186 /** 187 * Fill the given panel by adding all necessary components to the different 188 * locations. 189 * 190 * @param panel The container to fill. Must have an BorderLayout. 191 */ 192 public void fillPanel(Container panel) { 193 panel.add(this, BorderLayout.CENTER); 194 JToolBar jb = new JToolBar(JToolBar.VERTICAL); 195 jb.setFloatable(false); 196 jb.add(toolBarActions); 197 jb.addSeparator(); 198 jb.add(toolBarToggle); 199 panel.add(jb, BorderLayout.WEST); 200 if (statusLine != null) 201 panel.add(statusLine, BorderLayout.SOUTH); 202 } 203 203 } -
trunk/src/org/openstreetmap/josm/gui/MapMover.java
r1084 r1169 28 28 public class MapMover extends MouseAdapter implements MouseMotionListener, MouseWheelListener { 29 29 30 31 32 33 30 private final class ZoomerAction extends AbstractAction { 31 private final String action; 32 public ZoomerAction(String action) { 33 this.action = action; 34 34 } 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 } 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 35 public void actionPerformed(ActionEvent e) { 36 if (action.equals(".") || action.equals(",")) { 37 Point mouse = nc.getMousePosition(); 38 if (mouse == null) 39 mouse = new Point((int)nc.getBounds().getCenterX(), (int)nc.getBounds().getCenterY()); 40 MouseWheelEvent we = new MouseWheelEvent(nc, e.getID(), e.getWhen(), e.getModifiers(), mouse.x, mouse.y, 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 1, action.equals(",") ? -1 : 1); 41 mouseWheelMoved(we); 42 } else { 43 EastNorth center = nc.getCenter(); 44 EastNorth newcenter = nc.getEastNorth(nc.getWidth()/2+nc.getWidth()/5, nc.getHeight()/2+nc.getHeight()/5); 45 if (action.equals("left")) 46 nc.zoomTo(new EastNorth(2*center.east()-newcenter.east(), center.north()), nc.getScale()); 47 else if (action.equals("right")) 48 nc.zoomTo(new EastNorth(newcenter.east(), center.north()), nc.getScale()); 49 else if (action.equals("up")) 50 nc.zoomTo(new EastNorth(center.east(), 2*center.north()-newcenter.north()), nc.getScale()); 51 else if (action.equals("down")) 52 nc.zoomTo(new EastNorth(center.east(), newcenter.north()), nc.getScale()); 53 } 54 } 55 } 56 57 /** 58 * The point in the map that was the under the mouse point 59 * when moving around started. 60 */ 61 private EastNorth mousePosMove; 62 /** 63 * The map to move around. 64 */ 65 private final NavigatableComponent nc; 66 /** 67 * The old cursor when we changed it to movement cursor. 68 */ 69 private Cursor oldCursor; 70 71 private boolean movementInPlace = false; 72 73 /** 74 * Create a new MapMover 75 */ 76 public MapMover(NavigatableComponent navComp, JPanel contentPane) { 77 this.nc = navComp; 78 nc.addMouseListener(this); 79 nc.addMouseMotionListener(this); 80 nc.addMouseWheelListener(this); 81 82 if (contentPane != null) { 83 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 84 Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.GROUP_HOTKEY).getKeyStroke(), 85 "MapMover.Zoomer.right"); 86 contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right")); 87 88 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 89 Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.GROUP_HOTKEY).getKeyStroke(), 90 "MapMover.Zoomer.left"); 91 contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left")); 92 93 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 94 Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.GROUP_HOTKEY).getKeyStroke(), 95 "MapMover.Zoomer.up"); 96 contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up")); 97 98 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 99 Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.GROUP_HOTKEY).getKeyStroke(), 100 "MapMover.Zoomer.down"); 101 contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down")); 102 103 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 104 Shortcut.registerShortcut("view:zoominalternate", tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.GROUP_HOTKEY).getKeyStroke(), 105 "MapMover.Zoomer.in"); 106 contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(",")); 107 108 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 109 Shortcut.registerShortcut("view:zoomoutalternate", tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.GROUP_HOTKEY).getKeyStroke(), 110 "MapMover.Zoomer.out"); 111 contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction(".")); 112 } 113 } 114 115 /** 116 * If the right (and only the right) mouse button is pressed, move the map 117 */ 118 public void mouseDragged(MouseEvent e) { 119 int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK; 120 if ((e.getModifiersEx() & (MouseEvent.BUTTON3_DOWN_MASK | offMask)) == MouseEvent.BUTTON3_DOWN_MASK) { 121 if (mousePosMove == null) 122 startMovement(e); 123 EastNorth center = nc.getCenter(); 124 EastNorth mouseCenter = nc.getEastNorth(e.getX(), e.getY()); 125 EastNorth p = new EastNorth( 126 mousePosMove.east() + center.east() - mouseCenter.east(), 127 mousePosMove.north() + center.north() - mouseCenter.north()); 128 nc.zoomTo(p, nc.getScale()); 129 } else 130 endMovement(); 131 } 132 133 /** 134 * Start the movement, if it was the 3rd button (right button). 135 */ 136 @Override public void mousePressed(MouseEvent e) { 137 int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK; 138 if (e.getButton() == MouseEvent.BUTTON3 && (e.getModifiersEx() & offMask) == 0) 139 startMovement(e); 140 } 141 142 /** 143 * Change the cursor back to it's pre-move cursor. 144 */ 145 @Override public void mouseReleased(MouseEvent e) { 146 if (e.getButton() == MouseEvent.BUTTON3) 147 endMovement(); 148 } 149 150 /** 151 * Start movement by setting a new cursor and remember the current mouse 152 * position. 153 * @param e The mouse event that leat to the movement from. 154 */ 155 private void startMovement(MouseEvent e) { 156 if (movementInPlace) 157 return; 158 movementInPlace = true; 159 mousePosMove = nc.getEastNorth(e.getX(), e.getY()); 160 oldCursor = nc.getCursor(); 161 nc.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 162 } 163 164 /** 165 * End the movement. Setting back the cursor and clear the movement variables 166 */ 167 private void endMovement() { 168 if (!movementInPlace) 169 return; 170 movementInPlace = false; 171 if (oldCursor != null) 172 nc.setCursor(oldCursor); 173 else 174 nc.setCursor(Cursor.getDefaultCursor()); 175 mousePosMove = null; 176 oldCursor = null; 177 } 178 179 /** 180 * Zoom the map by 1/5th of current zoom per wheel-delta. 181 * @param e The wheel event. 182 */ 183 public void mouseWheelMoved(MouseWheelEvent e) { 184 int w = nc.getWidth(); 185 int h = nc.getHeight(); 186 187 double zoom = Math.max(0.1, 1 + e.getWheelRotation()/5.0); 188 double zoomfactor = (zoom-1)/2+1; 189 190 double newHalfWidth = w*zoomfactor - w/2; 191 double newHalfHeight = h*zoomfactor - h/2; 192 double centerx = e.getX() - (e.getX()-w/2)*newHalfWidth*2/w; 193 double centery = e.getY() - (e.getY()-h/2)*newHalfHeight*2/h; 194 EastNorth newCenter = nc.getEastNorth((int)centerx, (int)centery); 195 196 nc.zoomTo(newCenter, nc.getScale()*zoom); 197 } 198 199 /** 200 * Does nothing. Only to satisfy MouseMotionListener 201 */ 202 public void mouseMoved(MouseEvent e) {} 203 203 } -
trunk/src/org/openstreetmap/josm/gui/MapScaler.java
r999 r1169 18 18 public class MapScaler extends JComponent implements Helpful { 19 19 20 21 22 23 24 20 private final NavigatableComponent mv; 21 public MapScaler(NavigatableComponent mv, Projection proj) { 22 this.mv = mv; 23 setSize(100,30); 24 setOpaque(false); 25 25 } 26 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 27 @Override public void paint(Graphics g) { 28 LatLon ll1 = mv.getLatLon(0,0); 29 LatLon ll2 = mv.getLatLon(100,0); 30 double dist = ll1.greatCircleDistance(ll2); 31 String text = dist > 1000 ? (Math.round(dist/100)/10.0)+" km" : Math.round(dist*10)/10+" m"; 32 Rectangle2D bound = g.getFontMetrics().getStringBounds(text, g); 33 g.setColor(Main.pref.getColor(marktr("scale"), Color.white)); 34 g.drawLine(0, 5, 99, 5); 35 g.drawLine(0, 0, 0, 10); 36 g.drawLine(99, 0, 99, 10); 37 g.drawLine(49, 0, 49, 10); 38 g.drawLine(24, 3, 24, 7); 39 g.drawLine(74, 3, 74, 7); 40 g.drawString(text, (int)(50-bound.getWidth()/2), 23); 41 41 } 42 42 43 44 43 public String helpTopic() { 44 return "MapView/Scaler"; 45 45 } 46 46 } -
trunk/src/org/openstreetmap/josm/gui/MapSlider.java
r627 r1169 15 15 16 16 class MapSlider extends JSlider implements PropertyChangeListener, ChangeListener, Helpful { 17 17 18 18 private final MapView mv; 19 boolean clicked = false; 20 21 public MapSlider(MapView mv) { 22 super(0, 20); 23 setOpaque(false); 24 this.mv = mv; 25 addMouseListener(new MouseAdapter(){ 26 @Override public void mousePressed(MouseEvent e) { 27 clicked = true; 28 } 29 @Override public void mouseReleased(MouseEvent e) { 30 clicked = false; 31 } 32 }); 33 mv.addPropertyChangeListener("scale", this); 34 addChangeListener(this); 35 } 36 37 public void propertyChange(PropertyChangeEvent evt) { 38 if (!getModel().getValueIsAdjusting()) 39 setValue(this.mv.zoom()); 40 } 41 42 public void stateChanged(ChangeEvent e) { 43 if (!clicked) 44 return; 45 EastNorth pos = MapView.world; 46 for (int zoom = 0; zoom < getValue(); ++zoom) 47 pos = new EastNorth(pos.east()/2, pos.north()/2); 48 if (this.mv.getWidth() < this.mv.getHeight()) 49 this.mv.zoomTo(this.mv.center, pos.east()*2/(this.mv.getWidth()-20)); 50 else 51 this.mv.zoomTo(this.mv.center, pos.north()*2/(this.mv.getHeight()-20)); 52 } 19 boolean clicked = false; 53 20 54 public String helpTopic() { 55 return "MapView/Slider"; 21 public MapSlider(MapView mv) { 22 super(0, 20); 23 setOpaque(false); 24 this.mv = mv; 25 addMouseListener(new MouseAdapter(){ 26 @Override public void mousePressed(MouseEvent e) { 27 clicked = true; 28 } 29 @Override public void mouseReleased(MouseEvent e) { 30 clicked = false; 31 } 32 }); 33 mv.addPropertyChangeListener("scale", this); 34 addChangeListener(this); 35 } 36 37 public void propertyChange(PropertyChangeEvent evt) { 38 if (!getModel().getValueIsAdjusting()) 39 setValue(this.mv.zoom()); 40 } 41 42 public void stateChanged(ChangeEvent e) { 43 if (!clicked) 44 return; 45 EastNorth pos = MapView.world; 46 for (int zoom = 0; zoom < getValue(); ++zoom) 47 pos = new EastNorth(pos.east()/2, pos.north()/2); 48 if (this.mv.getWidth() < this.mv.getHeight()) 49 this.mv.zoomTo(this.mv.center, pos.east()*2/(this.mv.getWidth()-20)); 50 else 51 this.mv.zoomTo(this.mv.center, pos.north()*2/(this.mv.getHeight()-20)); 52 } 53 54 public String helpTopic() { 55 return "MapView/Slider"; 56 56 } 57 57 } -
trunk/src/org/openstreetmap/josm/gui/MapStatus.java
r1108 r1169 57 57 public class MapStatus extends JPanel implements Helpful { 58 58 59 60 61 62 63 64 /** 65 66 67 68 69 private JLabel tf; 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getMinimumSize().height); 88 89 90 91 59 /** 60 * The MapView this status belongs to. 61 */ 62 final MapView mv; 63 64 /** 65 * A small user interface component that consists of an image label and 66 * a fixed text content to the right of the image. 67 */ 68 class ImageLabel extends JPanel { 69 private JLabel tf; 70 private int chars; 71 public ImageLabel(String img, String tooltip, int chars) { 72 super(); 73 setLayout(new GridBagLayout()); 74 setBackground(Color.decode("#b8cfe5")); 75 add(new JLabel(ImageProvider.get("statusline/"+img+".png")), GBC.std().anchor(GBC.WEST).insets(0,1,1,0)); 76 add(tf = new JLabel(), GBC.std().fill(GBC.BOTH).anchor(GBC.WEST).insets(2,1,1,0)); 77 setToolTipText(tooltip); 78 this.chars = chars; 79 } 80 public void setText(String t) { 81 tf.setText(t); 82 } 83 @Override public Dimension getPreferredSize() { 84 return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getPreferredSize().height); 85 } 86 @Override public Dimension getMinimumSize() { 87 return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getMinimumSize().height); 88 } 89 } 90 91 LatLon.CoordinateFormat mCord; 92 92 93 93 ImageLabel lonText = new ImageLabel("lon", tr("The geographic longitude at the mouse pointer."), 11); … … 99 99 ImageLabel distText = new ImageLabel("dist", tr("The length of the new way segment being drawn."), 8); 100 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 101 /** 102 * The collector class that waits for notification and then update 103 * the display objects. 104 * 105 * @author imi 106 */ 107 private final class Collector implements Runnable { 108 /** 109 * The last object displayed in status line. 110 */ 111 Collection<OsmPrimitive> osmStatus; 112 /** 113 * The old modifiers that was pressed the last time this collector ran. 114 */ 115 private int oldModifiers; 116 /** 117 * The popup displayed to show additional information 118 */ 119 private Popup popup; 120 121 private MapFrame parent; 122 123 public Collector(MapFrame parent) { 124 this.parent = parent; 125 } 126 127 /** 128 * Execution function for the Collector. 129 */ 130 public void run() { 131 for (;;) { 132 MouseState ms = new MouseState(); 133 synchronized (this) { 134 try {wait();} catch (InterruptedException e) {} 135 ms.modifiers = mouseState.modifiers; 136 ms.mousePos = mouseState.mousePos; 137 } 138 if (parent != Main.map) 139 return; // exit, if new parent. 140 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null) 141 continue; // freeze display when holding down ctrl 142 143 if (mv.center == null) 144 continue; 145 146 // This try/catch is a hack to stop the flooding bug reports about this. 147 // The exception needed to handle with in the first place, means that this 148 // access to the data need to be restarted, if the main thread modifies 149 // the data. 150 try { 151 OsmPrimitive osmNearest = null; 152 // Set the text label in the bottom status bar 153 osmNearest = mv.getNearest(ms.mousePos); 154 if (osmNearest != null) { 155 NameVisitor visitor = new NameVisitor(); 156 osmNearest.visit(visitor); 157 nameText.setText(visitor.name); 158 } else 159 nameText.setText(tr("(no object)")); 160 161 // Popup Information 162 if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 ) { 163 Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos); 164 165 if (osms == null) 166 continue; 167 if (osms != null && osms.equals(osmStatus) && ms.modifiers == oldModifiers) 168 continue; 169 170 if (popup != null) { 171 try { 172 EventQueue.invokeAndWait(new Runnable() { 173 public void run() { 174 popup.hide(); 175 } 176 }); 177 177 } catch (InterruptedException e) { 178 178 } catch (InvocationTargetException e) { 179 179 throw new RuntimeException(e); 180 180 } 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 181 } 182 183 JPanel c = new JPanel(new GridBagLayout()); 184 for (final OsmPrimitive osm : osms) { 185 NameVisitor visitor = new NameVisitor(); 186 osm.visit(visitor); 187 final StringBuilder text = new StringBuilder(); 188 if (osm.id == 0 || osm.modified) 189 visitor.name = "<i><b>"+visitor.name+"*</b></i>"; 190 text.append(visitor.name); 191 if (osm.id != 0) 192 text.append("<br>id="+osm.id); 193 for (Entry<String, String> e : osm.entrySet()) 194 text.append("<br>"+e.getKey()+"="+e.getValue()); 195 final JLabel l = new JLabel("<html>"+text.toString()+"</html>", visitor.icon, JLabel.HORIZONTAL); 196 l.setFont(l.getFont().deriveFont(Font.PLAIN)); 197 l.setVerticalTextPosition(JLabel.TOP); 198 l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 199 l.addMouseListener(new MouseAdapter(){ 200 @Override public void mouseEntered(MouseEvent e) { 201 l.setText("<html><u color='blue'>"+text.toString()+"</u></html>"); 202 } 203 @Override public void mouseExited(MouseEvent e) { 204 l.setText("<html>"+text.toString()+"</html>"); 205 } 206 @Override public void mouseClicked(MouseEvent e) { 207 Main.ds.setSelected(osm); 208 mv.repaint(); 209 } 210 }); 211 c.add(l, GBC.eol()); 212 } 213 214 Point p = mv.getLocationOnScreen(); 215 popup = PopupFactory.getSharedInstance().getPopup(mv, c, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16); 216 final Popup staticPopup = popup; 217 EventQueue.invokeLater(new Runnable(){ 218 public void run() { 219 staticPopup.show(); 220 220 } 221 222 223 224 225 226 227 221 }); 222 } else if (popup != null) { 223 final Popup staticPopup = popup; 224 popup = null; 225 EventQueue.invokeLater(new Runnable(){ 226 public void run() { 227 staticPopup.hide(); 228 228 } 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 229 }); 230 } 231 } catch (ConcurrentModificationException x) { 232 } catch (NullPointerException x) { 233 } 234 } 235 } 236 } 237 238 /** 239 * Everything, the collector is interested of. Access must be synchronized. 240 * @author imi 241 */ 242 class MouseState { 243 Point mousePos; 244 int modifiers; 245 } 246 /** 247 * The last sent mouse movement event. 248 */ 249 MouseState mouseState = new MouseState(); 250 251 /** 252 * Construct a new MapStatus and attach it to the map view. 253 * @param mapFrame The MapFrame the status line is part of. 254 */ 255 public MapStatus(final MapFrame mapFrame) { 256 this.mv = mapFrame.mapView; 257 258 258 try { 259 259 mCord = LatLon.CoordinateFormat.valueOf(Main.pref.get("coordinates")); … … 261 261 mCord =LatLon.CoordinateFormat.DECIMAL_DEGREES; 262 262 } 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 263 // Listen for mouse movements and set the position text field 264 mv.addMouseMotionListener(new MouseMotionListener(){ 265 public void mouseDragged(MouseEvent e) { 266 mouseMoved(e); 267 } 268 public void mouseMoved(MouseEvent e) { 269 if (mv.center == null) 270 return; 271 // Do not update the view if ctrl is pressed. 272 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) { 273 LatLon p = mv.getLatLon(e.getX(),e.getY()); 274 latText.setText(p.latToString(mCord)); 275 lonText.setText(p.lonToString(mCord)); 276 } 277 } 278 }); 279 280 setLayout(new GridBagLayout()); 281 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 282 282 283 283 add(latText, GBC.std()); … … 287 287 add(distText, GBC.std().insets(3,0,0,0)); 288 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 289 helpText.setEditable(false); 290 add(nameText, GBC.std().insets(3,0,0,0)); 291 add(helpText, GBC.eol().insets(3,0,0,0).fill(GBC.HORIZONTAL)); 292 293 // The background thread 294 final Collector collector = new Collector(mapFrame); 295 new Thread(collector).start(); 296 297 // Listen to keyboard/mouse events for pressing/releasing alt key and 298 // inform the collector. 299 try { 300 Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){ 301 public void eventDispatched(AWTEvent event) { 302 synchronized (collector) { 303 mouseState.modifiers = ((InputEvent)event).getModifiersEx(); 304 if (event instanceof MouseEvent) 305 mouseState.mousePos = ((MouseEvent)event).getPoint(); 306 collector.notify(); 307 } 308 } 309 }, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); 310 } catch (SecurityException ex) { 311 mapFrame.mapView.addMouseMotionListener(new MouseMotionListener() { 312 public void mouseMoved(MouseEvent e) { 313 synchronized (collector) { 314 mouseState.modifiers = e.getModifiersEx(); 315 mouseState.mousePos = e.getPoint(); 316 collector.notify(); 317 } 318 } 319 320 public void mouseDragged(MouseEvent e) { 321 mouseMoved(e); 322 } 323 }); 324 325 mapFrame.mapView.addKeyListener(new KeyAdapter() { 326 @Override public void keyPressed(KeyEvent e) { 327 synchronized (collector) { 328 mouseState.modifiers = e.getModifiersEx(); 329 collector.notify(); 330 } 331 } 332 333 @Override public void keyReleased(KeyEvent e) { 334 keyPressed(e); 335 } 336 }); 337 } 338 } 339 340 public String helpTopic() { 341 return "Statusline"; 342 } 343 344 public void setHelpText(String t) { 345 helpText.setText(t); 346 helpText.setToolTipText(t); 347 } 348 public void setAngle(double a) { 349 angleText.setText(a < 0 ? "--" : Math.round(a*10)/10.0 + " °"); 350 } 351 public void setHeading(double h) { 352 headingText.setText(h < 0 ? "--" : Math.round(h*10)/10.0 + " °"); 353 } 354 public void setDist(double dist) { 355 String text = dist > 1000 ? (Math.round(dist/100)/10.0)+" km" : Math.round(dist*10)/10.0 +" m"; 356 distText.setText(dist < 0 ? "--" : text); 357 } 358 359 359 } -
trunk/src/org/openstreetmap/josm/gui/MapView.java
r1084 r1169 56 56 public class MapView extends NavigatableComponent { 57 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 * @param layerThe layer to move245 * @param posThe new position of the layer246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 58 /** 59 * Interface to notify listeners of the change of the active layer. 60 * @author imi 61 * @deprecated Use Layer.LayerChangeListener instead 62 */ 63 @Deprecated public interface LayerChangeListener { 64 void activeLayerChange(Layer oldLayer, Layer newLayer); 65 void layerAdded(Layer newLayer); 66 void layerRemoved(Layer oldLayer); 67 } 68 69 /** 70 * A list of all layers currently loaded. 71 */ 72 private ArrayList<Layer> layers = new ArrayList<Layer>(); 73 /** 74 * The play head marker: there is only one of these so it isn't in any specific layer 75 */ 76 public PlayHeadMarker playHeadMarker = null; 77 /** 78 * Direct link to the edit layer (if any) in the layers list. 79 */ 80 public OsmDataLayer editLayer; 81 /** 82 * The layer from the layers list that is currently active. 83 */ 84 private Layer activeLayer; 85 86 /** 87 * The last event performed by mouse. 88 */ 89 public MouseEvent lastMEvent; 90 91 private LinkedList<MapViewPaintable> temporaryLayers = new LinkedList<MapViewPaintable>(); 92 93 private BufferedImage offscreenBuffer; 94 95 /** 96 * The listener of the active layer changes. 97 * @deprecated Use Layer.listener instead. 98 */ 99 @Deprecated private Collection<LayerChangeListener> listeners = new LinkedList<LayerChangeListener>(); 100 101 public MapView() { 102 addComponentListener(new ComponentAdapter(){ 103 @Override public void componentResized(ComponentEvent e) { 104 removeComponentListener(this); 105 106 if (!zoomToEditLayerBoundingBox()) 107 new AutoScaleAction("data").actionPerformed(null); 108 109 new MapMover(MapView.this, Main.contentPane); 110 JosmAction mv; 111 mv = new MoveAction(MoveAction.Direction.UP); 112 if (mv.getShortcut() != null) { 113 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "UP"); 114 Main.contentPane.getActionMap().put("UP", mv); 115 } 116 mv = new MoveAction(MoveAction.Direction.DOWN); 117 if (mv.getShortcut() != null) { 118 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "DOWN"); 119 Main.contentPane.getActionMap().put("DOWN", mv); 120 } 121 mv = new MoveAction(MoveAction.Direction.LEFT); 122 if (mv.getShortcut() != null) { 123 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "LEFT"); 124 Main.contentPane.getActionMap().put("LEFT", mv); 125 } 126 mv = new MoveAction(MoveAction.Direction.RIGHT); 127 if (mv.getShortcut() != null) { 128 Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "RIGHT"); 129 Main.contentPane.getActionMap().put("RIGHT", mv); 130 } 131 132 MapSlider zoomSlider = new MapSlider(MapView.this); 133 add(zoomSlider); 134 zoomSlider.setBounds(3, 0, 114, 30); 135 136 MapScaler scaler = new MapScaler(MapView.this, Main.proj); 137 add(scaler); 138 scaler.setLocation(10,30); 139 } 140 }); 141 142 // listend to selection changes to redraw the map 143 DataSet.selListeners.add(new SelectionChangedListener(){ 144 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 145 repaint(); 146 } 147 }); 148 149 //store the last mouse action 150 this.addMouseMotionListener(new MouseMotionListener() { 151 public void mouseDragged(MouseEvent e) { 152 mouseMoved(e); 153 } 154 public void mouseMoved(MouseEvent e) { 155 lastMEvent = e; 156 } 157 }); 158 } 159 160 /** 161 * Add a layer to the current MapView. The layer will be added at topmost 162 * position. 163 */ 164 public void addLayer(Layer layer) { 165 if (layer instanceof OsmDataLayer) { 166 editLayer = (OsmDataLayer)layer; 167 Main.ds = editLayer.data; 168 editLayer.listenerModified.add(new ModifiedChangedListener(){ 169 public void modifiedChanged(boolean value, OsmDataLayer source) { 170 JOptionPane.getFrameForComponent(Main.parent).setTitle((value?"*":"")+tr("Java OpenStreetMap - Editor")); 171 } 172 }); 173 } 174 if (layer instanceof MarkerLayer && playHeadMarker == null) 175 playHeadMarker = PlayHeadMarker.create(); 176 int pos = layers.size(); 177 while(pos > 0 && layers.get(pos-1).background) 178 --pos; 179 layers.add(pos, layer); 180 181 // TODO: Deprecated 182 for (LayerChangeListener l : listeners) 183 l.layerAdded(layer); 184 for (Layer.LayerChangeListener l : Layer.listeners) 185 l.layerAdded(layer); 186 if (layer instanceof OsmDataLayer || activeLayer == null) { 187 // autoselect the new layer 188 Layer old = activeLayer; 189 setActiveLayer(layer); 190 for (Layer.LayerChangeListener l : Layer.listeners) 191 l.activeLayerChange(old, layer); 192 } 193 repaint(); 194 } 195 196 @Override 197 protected DataSet getData() 198 { 199 if(activeLayer != null && activeLayer instanceof OsmDataLayer) 200 return ((OsmDataLayer)activeLayer).data; 201 return new DataSet(); 202 } 203 204 public Boolean isDrawableLayer() 205 { 206 return activeLayer != null && activeLayer instanceof OsmDataLayer; 207 } 208 209 /** 210 * Remove the layer from the mapview. If the layer was in the list before, 211 * an LayerChange event is fired. 212 */ 213 public void removeLayer(Layer layer) { 214 if (layers.remove(layer)) { 215 // TODO: Deprecated 216 for (LayerChangeListener l : listeners) 217 l.layerRemoved(layer); 218 for (Layer.LayerChangeListener l : Layer.listeners) 219 l.layerRemoved(layer); 220 } 221 if (layer == editLayer) { 222 editLayer = null; 223 Main.ds.setSelected(); 224 } 225 layer.destroy(); 226 } 227 228 private Boolean virtualnodes = false; 229 public void enableVirtualNodes(Boolean state) 230 { 231 if(virtualnodes != state) 232 { 233 virtualnodes = state; 234 repaint(); 235 } 236 } 237 public Boolean useVirtualNodes() 238 { 239 return virtualnodes; 240 } 241 242 /** 243 * Moves the layer to the given new position. No event is fired. 244 * @param layer The layer to move 245 * @param pos The new position of the layer 246 */ 247 public void moveLayer(Layer layer, int pos) { 248 int curLayerPos = layers.indexOf(layer); 249 if (curLayerPos == -1) 250 throw new IllegalArgumentException(tr("layer not in list.")); 251 if (pos == curLayerPos) 252 return; // already in place. 253 layers.remove(curLayerPos); 254 if (pos >= layers.size()) 255 layers.add(layer); 256 else 257 layers.add(pos, layer); 258 } 259 260 /** 261 * Draw the component. 262 */ 263 @Override public void paint(Graphics g) { 264 if (center == null) 265 return; // no data loaded yet. 266 267 // re-create offscreen-buffer if we've been resized, otherwise 268 // just re-use it. 269 if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth() 270 || offscreenBuffer.getHeight() != getHeight()) 271 offscreenBuffer = new BufferedImage(getWidth(), getHeight(), 272 BufferedImage.TYPE_INT_ARGB); 273 274 Graphics2D tempG = offscreenBuffer.createGraphics(); 275 tempG.setColor(Main.pref.getColor("background", Color.BLACK)); 276 tempG.fillRect(0, 0, getWidth(), getHeight()); 277 278 for (int i = layers.size()-1; i >= 0; --i) { 279 Layer l = layers.get(i); 280 if (l.visible/* && l != getActiveLayer()*/) 281 l.paint(tempG, this); 282 } 283 284 /*if (getActiveLayer() != null && getActiveLayer().visible) 285 getActiveLayer().paint(tempG, this);*/ 286 287 for (MapViewPaintable mvp : temporaryLayers) { 288 mvp.paint(tempG, this); 289 } 290 291 // draw world borders 292 tempG.setColor(Color.WHITE); 293 Bounds b = new Bounds(); 294 Point min = getPoint(getProjection().latlon2eastNorth(b.min)); 295 Point max = getPoint(getProjection().latlon2eastNorth(b.max)); 296 int x1 = Math.min(min.x, max.x); 297 int y1 = Math.min(min.y, max.y); 298 int x2 = Math.max(min.x, max.x); 299 int y2 = Math.max(min.y, max.y); 300 if (x1 > 0 || y1 > 0 || x2 < getWidth() || y2 < getHeight()) 301 tempG.drawRect(x1, y1, x2-x1+1, y2-y1+1); 302 303 if (playHeadMarker != null) 304 playHeadMarker.paint(tempG, this); 305 306 g.drawImage(offscreenBuffer, 0, 0, null); 307 super.paint(g); 308 } 309 310 /** 311 * Set the new dimension to the projection class. Also adjust the components 312 * scale, if in autoScale mode. 313 */ 314 public void recalculateCenterScale(BoundingXYVisitor box) { 315 // -20 to leave some border 316 int w = getWidth()-20; 317 if (w < 20) 318 w = 20; 319 int h = getHeight()-20; 320 if (h < 20) 321 h = 20; 322 323 EastNorth oldCenter = center; 324 double oldScale = this.scale; 325 326 if (box == null || box.min == null || box.max == null || box.min.equals(box.max)) { 327 // no bounds means whole world 328 center = getProjection().latlon2eastNorth(new LatLon(0,0)); 329 EastNorth world = getProjection().latlon2eastNorth(new LatLon(Projection.MAX_LAT,Projection.MAX_LON)); 330 double scaleX = world.east()*2/w; 331 double scaleY = world.north()*2/h; 332 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen 333 } else { 334 center = new EastNorth(box.min.east()/2+box.max.east()/2, box.min.north()/2+box.max.north()/2); 335 double scaleX = (box.max.east()-box.min.east())/w; 336 double scaleY = (box.max.north()-box.min.north())/h; 337 scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen 338 } 339 340 if (!center.equals(oldCenter)) 341 firePropertyChange("center", oldCenter, center); 342 if (oldScale != scale) 343 firePropertyChange("scale", oldScale, scale); 344 repaint(); 345 } 346 347 /** 348 * Add a listener for changes of active layer. 349 * @param listener The listener that get added. 350 * @deprecated Use Layer.listener.add instead. 351 */ 352 @Deprecated public void addLayerChangeListener(LayerChangeListener listener) { 353 if (listener != null) 354 listeners.add(listener); 355 } 356 357 /** 358 * Remove the listener. 359 * @param listener The listener that get removed from the list. 360 * @deprecated Use Layer.listener.remove instead 361 */ 362 @Deprecated public void removeLayerChangeListener(LayerChangeListener listener) { 363 listeners.remove(listener); 364 } 365 366 /** 367 * @return An unmodificable list of all layers 368 */ 369 public Collection<Layer> getAllLayers() { 370 return Collections.unmodifiableCollection(layers); 371 } 372 373 /** 374 * Set the active selection to the given value and raise an layerchange event. 375 */ 376 public void setActiveLayer(Layer layer) { 377 if (!layers.contains(layer)) 378 throw new IllegalArgumentException("Layer must be in layerlist"); 379 if (layer instanceof OsmDataLayer) { 380 editLayer = (OsmDataLayer)layer; 381 Main.ds = editLayer.data; 382 } 383 else 384 Main.ds.setSelected(); 385 DataSet.fireSelectionChanged(Main.ds.getSelected()); 386 Layer old = activeLayer; 387 activeLayer = layer; 388 if (old != layer) { 389 // TODO: Deprecated 390 for (LayerChangeListener l : listeners) 391 l.activeLayerChange(old, layer); 392 for (Layer.LayerChangeListener l : Layer.listeners) 393 l.activeLayerChange(old, layer); 394 } 395 repaint(); 396 } 397 398 /** 399 * @return The current active layer 400 */ 401 public Layer getActiveLayer() { 402 return activeLayer; 403 } 404 405 /** 406 * In addition to the base class funcitonality, this keep trak of the autoscale 407 * feature. 408 */ 409 @Override public void zoomTo(EastNorth newCenter, double scale) { 410 EastNorth oldCenter = center; 411 double oldScale = this.scale; 412 super.zoomTo(newCenter, scale); 413 if ((oldCenter == null && center != null) || !oldCenter.equals(center)) 414 firePropertyChange("center", oldCenter, center); 415 if (oldScale != scale) 416 firePropertyChange("scale", oldScale, scale); 417 } 418 419 /** 420 * Tries to zoom to the download boundingbox[es] of the current edit layer 421 * (aka {@link OsmDataLayer}). If the edit layer has multiple download bounding 422 * boxes it zooms to a large virtual bounding box containing all smaller ones. 423 * This implementation can be used for resolving ticket #1461. 424 * 425 * @return <code>true</code> if a zoom operation has been performed 426 * @author Jan Peter Stotz 427 */ 428 public boolean zoomToEditLayerBoundingBox() { 429 // workaround for #1461 (zoom to download bounding box instead of all data) 430 // In case we already have an existing data layer ... 431 Collection<DataSource> dataSources = Main.main.editLayer().data.dataSources; 432 // ... with bounding box[es] of data loaded from OSM or a file... 433 BoundingXYVisitor bbox = new BoundingXYVisitor(); 434 for (DataSource ds : dataSources) { 435 if (ds.bounds != null) { 436 bbox.visit(Main.proj.latlon2eastNorth(ds.bounds.max)); 437 bbox.visit(Main.proj.latlon2eastNorth(ds.bounds.min)); 438 } 439 if (bbox.max != null && bbox.min != null && !bbox.max.equals(bbox.min)) { 440 // ... we zoom to it's bounding box 441 recalculateCenterScale(bbox); 442 return true; 443 } 444 } 445 return false; 446 } 447 448 public boolean addTemporaryLayer(MapViewPaintable mvp) { 449 if (temporaryLayers.contains(mvp)) return false; 450 return temporaryLayers.add(mvp); 451 } 452 453 public boolean removeTemporaryLayer(MapViewPaintable mvp) { 454 return temporaryLayers.remove(mvp); 455 } 456 456 } -
trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
r860 r1169 33 33 public class NavigatableComponent extends JComponent implements Helpful { 34 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 *change the center by accessing the return value. Use zoomTo instead.82 83 84 85 86 87 88 89 90 91 92 *on the screen.93 94 95 96 97 98 99 100 101 102 103 104 105 *on the screen.106 107 108 109 110 111 112 113 114 115 116 *to the own top/left.117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 * 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 * @return the nearest way segment to the screen point given that is not 214 215 * 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 if (osm == null) 269 270 271 272 273 274 275 276 277 278 *if no item under or near the point. The returned279 *list is never empty.280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 *if no node under or near the point. The returned320 *list is never empty.321 322 323 324 325 326 327 328 329 330 331 332 333 334 * @return the nearest nodes to the screen point given that is not 335 336 * 337 338 339 340 341 342 35 public static final EastNorth world = Main.proj.latlon2eastNorth(new LatLon(Projection.MAX_LAT, Projection.MAX_LON)); 36 public static final int snapDistance = sqr(Main.pref.getInteger("node.snap-distance", 10)); 37 38 private static int sqr(int a) { return a*a;} 39 /** 40 * The scale factor in x or y-units per pixel. This means, if scale = 10, 41 * every physical pixel on screen are 10 x or 10 y units in the 42 * northing/easting space of the projection. 43 */ 44 protected double scale; 45 /** 46 * Center n/e coordinate of the desired screen center. 47 */ 48 protected EastNorth center; 49 50 public NavigatableComponent() { 51 setLayout(null); 52 } 53 54 protected DataSet getData() 55 { 56 return Main.ds; 57 } 58 59 /** 60 * Return the OSM-conform zoom factor (0 for whole world, 1 for half, 2 for quarter...) 61 */ 62 public int zoom() { 63 double sizex = scale * getWidth(); 64 double sizey = scale * getHeight(); 65 for (int zoom = 0; zoom <= 32; zoom++, sizex *= 2, sizey *= 2) 66 if (sizex > world.east() || sizey > world.north()) 67 return zoom; 68 return 32; 69 } 70 71 /** 72 * Return the current scale value. 73 * @return The scale value currently used in display 74 */ 75 public double getScale() { 76 return scale; 77 } 78 79 /** 80 * @return Returns the center point. A copy is returned, so users cannot 81 * change the center by accessing the return value. Use zoomTo instead. 82 */ 83 public EastNorth getCenter() { 84 return center; 85 } 86 87 /** 88 * @param x X-Pixelposition to get coordinate from 89 * @param y Y-Pixelposition to get coordinate from 90 * 91 * @return Geographic coordinates from a specific pixel coordination 92 * on the screen. 93 */ 94 public EastNorth getEastNorth(int x, int y) { 95 return new EastNorth( 96 center.east() + (x - getWidth()/2.0)*scale, 97 center.north() - (y - getHeight()/2.0)*scale); 98 } 99 100 /** 101 * @param x X-Pixelposition to get coordinate from 102 * @param y Y-Pixelposition to get coordinate from 103 * 104 * @return Geographic unprojected coordinates from a specific pixel coordination 105 * on the screen. 106 */ 107 public LatLon getLatLon(int x, int y) { 108 109 return getProjection().eastNorth2latlon(getEastNorth(x, y)); 110 } 111 112 /** 113 * Return the point on the screen where this Coordinate would be. 114 * @param p The point, where this geopoint would be drawn. 115 * @return The point on screen where "point" would be drawn, relative 116 * to the own top/left. 117 */ 118 public Point getPoint(EastNorth p) { 119 if(null == p) 120 return new Point(); 121 double x = (p.east()-center.east())/scale + getWidth()/2; 122 double y = (center.north()-p.north())/scale + getHeight()/2; 123 return new Point((int)x,(int)y); 124 } 125 126 /** 127 * Zoom to the given coordinate. 128 * @param newCenter The center x-value (easting) to zoom to. 129 * @param scale The scale to use. 130 */ 131 public void zoomTo(EastNorth newCenter, double scale) { 132 center = newCenter; 133 getProjection().eastNorth2latlon(center); 134 this.scale = scale; 135 repaint(); 136 } 137 138 /** 139 * Return the nearest point to the screen point given. 140 * If a node within 10 pixel is found, the nearest node is returned. 141 */ 142 public final Node getNearestNode(Point p) { 143 double minDistanceSq = Double.MAX_VALUE; 144 Node minPrimitive = null; 145 for (Node n : getData().nodes) { 146 if (n.deleted || n.incomplete) 147 continue; 148 Point sp = getPoint(n.eastNorth); 149 double dist = p.distanceSq(sp); 150 if (minDistanceSq > dist && dist < snapDistance) { 151 minDistanceSq = p.distanceSq(sp); 152 minPrimitive = n; 153 } 154 // prefer already selected node when multiple nodes on one point 155 else if(minDistanceSq == dist && n.selected && !minPrimitive.selected) 156 { 157 minPrimitive = n; 158 } 159 } 160 return minPrimitive; 161 } 162 163 /** 164 * @return all way segments within 10px of p, sorted by their 165 * perpendicular distance. 166 * 167 * @param p the point for which to search the nearest segment. 168 */ 169 public final List<WaySegment> getNearestWaySegments(Point p) { 170 TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>(); 171 for (Way w : getData().ways) { 172 if (w.deleted || w.incomplete) continue; 173 Node lastN = null; 174 int i = -2; 175 for (Node n : w.nodes) { 176 i++; 177 if (n.deleted || n.incomplete) continue; 178 if (lastN == null) { 179 lastN = n; 180 continue; 181 } 182 183 Point A = getPoint(lastN.eastNorth); 184 Point B = getPoint(n.eastNorth); 185 double c = A.distanceSq(B); 186 double a = p.distanceSq(B); 187 double b = p.distanceSq(A); 188 double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared 189 if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) { 190 if(w.selected) // prefer selected ways a little bit 191 perDist -= 0.00001; 192 List<WaySegment> l; 193 if (nearest.containsKey(perDist)) { 194 l = nearest.get(perDist); 195 } else { 196 l = new LinkedList<WaySegment>(); 197 nearest.put(perDist, l); 198 } 199 l.add(new WaySegment(w, i)); 200 } 201 202 lastN = n; 203 } 204 } 205 ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>(); 206 for (List<WaySegment> wss : nearest.values()) { 207 nearestList.addAll(wss); 208 } 209 return nearestList; 210 } 211 212 /** 213 * @return the nearest way segment to the screen point given that is not 214 * in ignore. 215 * 216 * @param p the point for which to search the nearest segment. 217 * @param ignore a collection of segments which are not to be returned. 218 * May be null. 219 */ 220 public final WaySegment getNearestWaySegment(Point p, Collection<WaySegment> ignore) { 221 List<WaySegment> nearest = getNearestWaySegments(p); 222 if (ignore != null) nearest.removeAll(ignore); 223 return nearest.isEmpty() ? null : nearest.get(0); 224 } 225 226 /** 227 * @return the nearest way segment to the screen point given. 228 */ 229 public final WaySegment getNearestWaySegment(Point p) { 230 return getNearestWaySegment(p, null); 231 } 232 233 /** 234 * @return the nearest way to the screen point given. 235 */ 236 public final Way getNearestWay(Point p) { 237 WaySegment nearestWaySeg = getNearestWaySegment(p); 238 return nearestWaySeg == null ? null : nearestWaySeg.way; 239 } 240 241 /** 242 * Return the object, that is nearest to the given screen point. 243 * 244 * First, a node will be searched. If a node within 10 pixel is found, the 245 * nearest node is returned. 246 * 247 * If no node is found, search for near ways. 248 * 249 * If nothing is found, return <code>null</code>. 250 * 251 * @param p The point on screen. 252 * @return The primitive that is nearest to the point p. 253 */ 254 public OsmPrimitive getNearest(Point p) { 255 OsmPrimitive osm = getNearestNode(p); 256 if (osm == null) 257 { 258 osm = getNearestWay(p); 259 } 260 return osm; 261 } 262 263 /** 264 * Returns a singleton of the nearest object, or else an empty collection. 265 */ 266 public Collection<OsmPrimitive> getNearestCollection(Point p) { 267 OsmPrimitive osm = getNearest(p); 268 if (osm == null) 269 return Collections.emptySet(); 270 return Collections.singleton(osm); 271 } 272 273 /** 274 * @return A list of all objects that are nearest to 275 * the mouse. Does a simple sequential scan on all the data. 276 * 277 * @return A collection of all items or <code>null</code> 278 * if no item under or near the point. The returned 279 * list is never empty. 280 */ 281 public Collection<OsmPrimitive> getAllNearest(Point p) { 282 Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>(); 283 for (Way w : getData().ways) { 284 if (w.deleted || w.incomplete) continue; 285 Node lastN = null; 286 for (Node n : w.nodes) { 287 if (n.deleted || n.incomplete) continue; 288 if (lastN == null) { 289 lastN = n; 290 continue; 291 } 292 Point A = getPoint(lastN.eastNorth); 293 Point B = getPoint(n.eastNorth); 294 double c = A.distanceSq(B); 295 double a = p.distanceSq(B); 296 double b = p.distanceSq(A); 297 double perDist = a-(a-b+c)*(a-b+c)/4/c; // perpendicular distance squared 298 if (perDist < snapDistance && a < c+snapDistance && b < c+snapDistance) { 299 nearest.add(w); 300 break; 301 } 302 lastN = n; 303 } 304 } 305 for (Node n : getData().nodes) { 306 if (!n.deleted && !n.incomplete 307 && getPoint(n.eastNorth).distanceSq(p) < snapDistance) { 308 nearest.add(n); 309 } 310 } 311 return nearest.isEmpty() ? null : nearest; 312 } 313 314 /** 315 * @return A list of all nodes that are nearest to 316 * the mouse. Does a simple sequential scan on all the data. 317 * 318 * @return A collection of all nodes or <code>null</code> 319 * if no node under or near the point. The returned 320 * list is never empty. 321 */ 322 public Collection<Node> getNearestNodes(Point p) { 323 Collection<Node> nearest = new HashSet<Node>(); 324 for (Node n : getData().nodes) { 325 if (!n.deleted && !n.incomplete 326 && getPoint(n.eastNorth).distanceSq(p) < snapDistance) { 327 nearest.add(n); 328 } 329 } 330 return nearest.isEmpty() ? null : nearest; 331 } 332 333 /** 334 * @return the nearest nodes to the screen point given that is not 335 * in ignore. 336 * 337 * @param p the point for which to search the nearest segment. 338 * @param ignore a collection of nodes which are not to be returned. 339 * May be null. 340 */ 341 public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore) { 342 Collection<Node> nearest = getNearestNodes(p); 343 343 if (nearest == null) return null; 344 345 346 347 348 349 350 351 352 353 354 355 356 357 344 if (ignore != null) nearest.removeAll(ignore); 345 return nearest.isEmpty() ? null : nearest; 346 } 347 348 /** 349 * @return The projection to be used in calculating stuff. 350 */ 351 protected Projection getProjection() { 352 return Main.proj; 353 } 354 355 public String helpTopic() { 356 String n = getClass().getName(); 357 return n.substring(n.lastIndexOf('.')+1); 358 358 } 359 359 } -
trunk/src/org/openstreetmap/josm/gui/OsmPrimitivRenderer.java
r627 r1169 17 17 /** 18 18 * Renderer that renders the objects from an OsmPrimitive as data. 19 * 19 * 20 20 * Can be used in lists and tables. 21 * 21 * 22 22 * @author imi 23 23 * @author Frederik Ramm <frederik@remote.org> … … 25 25 public class OsmPrimitivRenderer implements ListCellRenderer, TableCellRenderer { 26 26 27 28 29 30 27 /** 28 * NameVisitor provides proper names and icons for OsmPrimitives 29 */ 30 private NameVisitor visitor = new NameVisitor(); 31 31 32 /** 33 * Default list cell renderer - delegate for ListCellRenderer operation 34 */ 35 private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer(); 36 37 /** 38 * Default table cell renderer - delegate for TableCellRenderer operation 39 */ 40 private DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer(); 32 /** 33 * Default list cell renderer - delegate for ListCellRenderer operation 34 */ 35 private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer(); 41 36 42 /** 43 * Adapter method supporting the ListCellRenderer interface. 44 */ 45 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 46 Component def = defaultListCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 47 return renderer(def, (OsmPrimitive) value); 48 } 37 /** 38 * Default table cell renderer - delegate for TableCellRenderer operation 39 */ 40 private DefaultTableCellRenderer defaultTableCellRenderer = new DefaultTableCellRenderer(); 49 41 50 /** 51 * Adapter method supporting the TableCellRenderer interface. 52 */ 53 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 54 Component def = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 55 return renderer(def, (OsmPrimitive) value); 56 } 57 58 /** 59 * Internal method that stuffs information into the rendering component 60 * provided that it's a kind of JLabel. 61 * @param def the rendering component 62 * @param value the OsmPrimtive to render 63 * @return the modified rendering component 64 */ 65 private Component renderer(Component def, OsmPrimitive value) { 66 if (def != null && value != null && def instanceof JLabel) { 67 (value).visit(visitor); 68 ((JLabel)def).setText(visitor.name); 69 ((JLabel)def).setIcon(visitor.icon); 70 } 71 return def; 72 } 73 42 /** 43 * Adapter method supporting the ListCellRenderer interface. 44 */ 45 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 46 Component def = defaultListCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 47 return renderer(def, (OsmPrimitive) value); 48 } 49 50 /** 51 * Adapter method supporting the TableCellRenderer interface. 52 */ 53 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 54 Component def = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 55 return renderer(def, (OsmPrimitive) value); 56 } 57 58 /** 59 * Internal method that stuffs information into the rendering component 60 * provided that it's a kind of JLabel. 61 * @param def the rendering component 62 * @param value the OsmPrimtive to render 63 * @return the modified rendering component 64 */ 65 private Component renderer(Component def, OsmPrimitive value) { 66 if (def != null && value != null && def instanceof JLabel) { 67 (value).visit(visitor); 68 ((JLabel)def).setText(visitor.name); 69 ((JLabel)def).setIcon(visitor.icon); 70 } 71 return def; 72 } 73 74 74 } -
trunk/src/org/openstreetmap/josm/gui/PleaseWaitDialog.java
r1073 r1169 20 20 public class PleaseWaitDialog extends JDialog { 21 21 22 22 private final JProgressBar progressBar = new JProgressBar(); 23 23 24 25 26 24 public final JLabel currentAction = new JLabel(I18n.tr("Contacting the OSM server...")); 25 public final BoundedRangeModel progress = progressBar.getModel(); 26 public final JButton cancel = new JButton(I18n.tr("Cancel")); 27 27 28 29 30 31 32 33 34 35 36 37 38 39 28 public PleaseWaitDialog(Component parent) { 29 super(JOptionPane.getFrameForComponent(parent), true); 30 setLayout(new GridBagLayout()); 31 JPanel pane = new JPanel(new GridBagLayout()); 32 pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); 33 pane.add(currentAction, GBC.eol().fill(GBC.HORIZONTAL)); 34 pane.add(progressBar, GBC.eop().fill(GBC.HORIZONTAL)); 35 pane.add(cancel, GBC.eol().anchor(GBC.CENTER)); 36 setContentPane(pane); 37 setSize(Main.pref.getInteger("progressdialog.size",400),100); 38 setLocationRelativeTo(Main.parent); 39 } 40 40 } -
trunk/src/org/openstreetmap/josm/gui/PleaseWaitRunnable.java
r627 r1169 26 26 public abstract class PleaseWaitRunnable implements Runnable { 27 27 28 28 public String errorMessage; 29 29 30 31 30 private boolean closeDialogCalled = false; 31 private boolean cancelled = false; 32 32 33 33 private final String title; 34 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 35 /** 36 * Create the runnable object with a given message for the user. 37 */ 38 public PleaseWaitRunnable(String title) { 39 this.title = title; 40 Main.pleaseWaitDlg.cancel.addActionListener(new ActionListener(){ 41 public void actionPerformed(ActionEvent e) { 42 if (!cancelled) { 43 cancelled = true; 44 cancel(); 45 } 46 } 47 }); 48 Main.pleaseWaitDlg.addWindowListener(new WindowAdapter(){ 49 @Override public void windowClosing(WindowEvent e) { 50 if (!closeDialogCalled) { 51 if (!cancelled) { 52 cancelled = true; 53 cancel(); 54 } 55 closeDialog(); 56 } 57 } 58 }); 59 } 60 60 61 62 63 64 61 public final void run() { 62 try { 63 if (cancelled) 64 return; // since realRun isn't executed, do not call to finish 65 65 66 67 68 69 66 // reset dialog state 67 Main.pleaseWaitDlg.setTitle(title); 68 errorMessage = null; 69 closeDialogCalled = false; 70 70 71 72 73 74 75 76 77 78 79 80 81 82 71 // show the dialog 72 synchronized (this) { 73 EventQueue.invokeLater(new Runnable() { 74 public void run() { 75 synchronized (PleaseWaitRunnable.this) { 76 PleaseWaitRunnable.this.notifyAll(); 77 } 78 Main.pleaseWaitDlg.setVisible(true); 79 } 80 }); 81 try {wait();} catch (InterruptedException e) {} 82 } 83 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 84 realRun(); 85 } catch (SAXException x) { 86 x.printStackTrace(); 87 errorMessage = tr("Error while parsing")+": "+x.getMessage(); 88 } catch (FileNotFoundException x) { 89 x.printStackTrace(); 90 errorMessage = tr("File not found")+": "+x.getMessage(); 91 } catch (IOException x) { 92 x.printStackTrace(); 93 errorMessage = x.getMessage(); 94 } finally { 95 closeDialog(); 96 } 97 } 98 98 99 100 101 102 99 /** 100 * User pressed cancel button. 101 */ 102 protected abstract void cancel(); 103 103 104 105 106 107 108 109 104 /** 105 * Called in the worker thread to do the actual work. When any of the 106 * exception is thrown, a message box will be displayed and closeDialog 107 * is called. finish() is called in any case. 108 */ 109 protected abstract void realRun() throws SAXException, IOException; 110 110 111 112 113 114 115 111 /** 112 * Finish up the data work. Is guaranteed to be called if realRun is called. 113 * Finish is called in the gui thread just after the dialog disappeared. 114 */ 115 protected abstract void finish(); 116 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 117 /** 118 * Close the dialog. Usually called from worker thread. 119 */ 120 public void closeDialog() { 121 if (closeDialogCalled) 122 return; 123 closeDialogCalled = true; 124 try { 125 Runnable runnable = new Runnable(){ 126 public void run() { 127 try { 128 finish(); 129 } finally { 130 Main.pleaseWaitDlg.setVisible(false); 131 Main.pleaseWaitDlg.dispose(); 132 } 133 if (errorMessage != null) 134 JOptionPane.showMessageDialog(Main.parent, errorMessage); 135 } 136 }; 137 137 138 139 140 141 142 138 // make sure, this is called in the dispatcher thread ASAP 139 if (EventQueue.isDispatchThread()) 140 runnable.run(); 141 else 142 EventQueue.invokeAndWait(runnable); 143 143 144 145 146 147 148 144 } catch (InterruptedException e) { 145 } catch (InvocationTargetException e) { 146 throw new RuntimeException(e); 147 } 148 } 149 149 } -
trunk/src/org/openstreetmap/josm/gui/QuadStateCheckBox.java
r747 r1169 23 23 public class QuadStateCheckBox extends JCheckBox { 24 24 25 25 public enum State { NOT_SELECTED, SELECTED, UNSET, PARTIAL } 26 26 27 28 27 private final QuadStateDecorator model; 28 private State[] allowed; 29 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 30 public QuadStateCheckBox(String text, Icon icon, State initial, State[] allowed) { 31 super(text, icon); 32 this.allowed = allowed; 33 // Add a listener for when the mouse is pressed 34 super.addMouseListener(new MouseAdapter() { 35 @Override public void mousePressed(MouseEvent e) { 36 grabFocus(); 37 model.nextState(); 38 } 39 }); 40 // Reset the keyboard action map 41 ActionMap map = new ActionMapUIResource(); 42 map.put("pressed", new AbstractAction() { 43 public void actionPerformed(ActionEvent e) { 44 grabFocus(); 45 model.nextState(); 46 } 47 }); 48 map.put("released", null); 49 SwingUtilities.replaceUIActionMap(this, map); 50 // set the model to the adapted model 51 model = new QuadStateDecorator(getModel()); 52 setModel(model); 53 setState(initial); 54 } 55 public QuadStateCheckBox(String text, State initial, State[] allowed) { 56 this(text, null, initial, allowed); 57 } 58 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 59 /** Do not let anyone add mouse listeners */ 60 @Override public void addMouseListener(MouseListener l) { } 61 /** 62 * Set the new state. 63 */ 64 public void setState(State state) { model.setState(state); } 65 /** Return the current state, which is determined by the 66 * selection status of the model. */ 67 public State getState() { return model.getState(); } 68 @Override public void setSelected(boolean b) { 69 if (b) { 70 setState(State.SELECTED); 71 } else { 72 setState(State.NOT_SELECTED); 73 } 74 } 75 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 * 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 76 private class QuadStateDecorator implements ButtonModel { 77 private final ButtonModel other; 78 private QuadStateDecorator(ButtonModel other) { 79 this.other = other; 80 } 81 private void setState(State state) { 82 if (state == State.NOT_SELECTED) { 83 other.setArmed(false); 84 other.setPressed(false); 85 other.setSelected(false); 86 setToolTipText(tr("false: the property is explicitly switched off")); 87 } else if (state == State.SELECTED) { 88 other.setArmed(false); 89 other.setPressed(false); 90 other.setSelected(true); 91 setToolTipText(tr("true: the property is explicitly switched on")); 92 } else if (state == State.PARTIAL) { 93 other.setArmed(true); 94 other.setPressed(true); 95 other.setSelected(true); 96 setToolTipText(tr("partial: different selected objects have different values, do not change")); 97 } else { 98 other.setArmed(true); 99 other.setPressed(true); 100 other.setSelected(false); 101 setToolTipText(tr("unset: do not set this property on the selected objects")); 102 } 103 } 104 /** 105 * The current state is embedded in the selection / armed 106 * state of the model. 107 * 108 * We return the SELECTED state when the checkbox is selected 109 * but not armed, PARTIAL state when the checkbox is 110 * selected and armed (grey) and NOT_SELECTED when the 111 * checkbox is deselected. 112 */ 113 private State getState() { 114 if (isSelected() && !isArmed()) { 115 // normal black tick 116 return State.SELECTED; 117 } else if (isSelected() && isArmed()) { 118 // don't care grey tick 119 return State.PARTIAL; 120 } else if (!isSelected() && !isArmed()) { 121 return State.NOT_SELECTED; 122 } else { 123 return State.UNSET; 124 } 125 } 126 /** Rotate to the next allowed state.*/ 127 private void nextState() { 128 State current = getState(); 129 for (int i = 0; i < allowed.length; i++) { 130 if (allowed[i] == current) { 131 setState((i == allowed.length-1) ? allowed[0] : allowed[i+1]); 132 break; 133 } 134 } 135 } 136 /** Filter: No one may change the armed/selected/pressed status except us. */ 137 public void setArmed(boolean b) { } 138 public void setSelected(boolean b) { } 139 public void setPressed(boolean b) { } 140 /** We disable focusing on the component when it is not 141 * enabled. */ 142 public void setEnabled(boolean b) { 143 setFocusable(b); 144 other.setEnabled(b); 145 } 146 /** All these methods simply delegate to the "other" model 147 * that is being decorated. */ 148 public boolean isArmed() { return other.isArmed(); } 149 public boolean isSelected() { return other.isSelected(); } 150 public boolean isEnabled() { return other.isEnabled(); } 151 public boolean isPressed() { return other.isPressed(); } 152 public boolean isRollover() { return other.isRollover(); } 153 public void setRollover(boolean b) { other.setRollover(b); } 154 public void setMnemonic(int key) { other.setMnemonic(key); } 155 public int getMnemonic() { return other.getMnemonic(); } 156 public void setActionCommand(String s) { 157 other.setActionCommand(s); 158 } 159 public String getActionCommand() { 160 return other.getActionCommand(); 161 } 162 public void setGroup(ButtonGroup group) { 163 other.setGroup(group); 164 } 165 public void addActionListener(ActionListener l) { 166 other.addActionListener(l); 167 } 168 public void removeActionListener(ActionListener l) { 169 other.removeActionListener(l); 170 } 171 public void addItemListener(ItemListener l) { 172 other.addItemListener(l); 173 } 174 public void removeItemListener(ItemListener l) { 175 other.removeItemListener(l); 176 } 177 public void addChangeListener(ChangeListener l) { 178 other.addChangeListener(l); 179 } 180 public void removeChangeListener(ChangeListener l) { 181 other.removeChangeListener(l); 182 } 183 public Object[] getSelectedObjects() { 184 return other.getSelectedObjects(); 185 } 186 } 187 187 } 188 188 -
trunk/src/org/openstreetmap/josm/gui/SelectionManager.java
r1003 r1169 23 23 * Manages the selection of a rectangle. Listening to left and right mouse button 24 24 * presses and to mouse motions and draw the rectangle accordingly. 25 * 25 * 26 26 * Left mouse button selects a rectangle from the press until release. Pressing 27 27 * right mouse button while left is still pressed enable the rectangle to move … … 29 29 * at constructor, except if the right is still pressed, which just remove the 30 30 * selection rectangle and does nothing. 31 * 32 * The point where the left mouse button was pressed and the current mouse 31 * 32 * The point where the left mouse button was pressed and the current mouse 33 33 * position are two opposite corners of the selection rectangle. 34 * 35 * It is possible to specify an aspect ratio (width per height) which the 34 * 35 * It is possible to specify an aspect ratio (width per height) which the 36 36 * selection rectangle always must have. In this case, the selection rectangle 37 37 * will be the largest window with this aspect ratio, where the position the left 38 * mouse button was pressed and the corner of the current mouse position are at 38 * mouse button was pressed and the corner of the current mouse position are at 39 39 * opposite sites (the mouse position corner is the corner nearest to the mouse 40 * cursor). 41 * 42 * When the left mouse button was released, an ActionEvent is send to the 40 * cursor). 41 * 42 * When the left mouse button was released, an ActionEvent is send to the 43 43 * ActionListener given at constructor. The source of this event is this manager. 44 * 44 * 45 45 * @author imi 46 46 */ 47 47 public class SelectionManager implements MouseListener, MouseMotionListener, PropertyChangeListener { 48 48 49 50 51 52 53 54 55 56 57 58 59 60 * @param ctrl Whether the ctrl key was pressed 61 62 63 64 65 66 67 68 69 70 * Called to remove the selection manager from the listener list 71 72 73 74 75 76 77 78 79 80 81 82 * If this is not <code>null</code>, a rectangle is drawn on screen. 83 84 85 86 87 88 89 90 91 92 93 94 * Whether the selection rectangle must obtain the aspect ratio of the 95 96 97 98 99 100 101 102 103 *the left button is released.104 105 *ratio of the drawComponent.106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 49 /** 50 * This is the interface that an user of SelectionManager has to implement 51 * to get informed when a selection closes. 52 * @author imi 53 */ 54 public interface SelectionEnded { 55 /** 56 * Called, when the left mouse button was released. 57 * @param r The rectangle that is currently the selection. 58 * @param alt Whether the alt key was pressed 59 * @param shift Whether the shift key was pressed 60 * @param ctrl Whether the ctrl key was pressed 61 * @see InputEvent#getModifiersEx() 62 */ 63 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl); 64 /** 65 * Called to register the selection manager for "active" property. 66 * @param listener The listener to register 67 */ 68 public void addPropertyChangeListener(PropertyChangeListener listener); 69 /** 70 * Called to remove the selection manager from the listener list 71 * for "active" property. 72 * @param listener The listener to register 73 */ 74 public void removePropertyChangeListener(PropertyChangeListener listener); 75 } 76 /** 77 * The listener that receives the events after left mouse button is released. 78 */ 79 private final SelectionEnded selectionEndedListener; 80 /** 81 * Position of the map when the mouse button was pressed. 82 * If this is not <code>null</code>, a rectangle is drawn on screen. 83 */ 84 private Point mousePosStart; 85 /** 86 * Position of the map when the selection rectangle was last drawn. 87 */ 88 private Point mousePos; 89 /** 90 * The Component, the selection rectangle is drawn onto. 91 */ 92 private final NavigatableComponent nc; 93 /** 94 * Whether the selection rectangle must obtain the aspect ratio of the 95 * drawComponent. 96 */ 97 private boolean aspectRatio; 98 99 /** 100 * Create a new SelectionManager. 101 * 102 * @param selectionEndedListener The action listener that receives the event when 103 * the left button is released. 104 * @param aspectRatio If true, the selection window must obtain the aspect 105 * ratio of the drawComponent. 106 * @param navComp The component, the rectangle is drawn onto. 107 */ 108 public SelectionManager(SelectionEnded selectionEndedListener, boolean aspectRatio, NavigatableComponent navComp) { 109 this.selectionEndedListener = selectionEndedListener; 110 this.aspectRatio = aspectRatio; 111 this.nc = navComp; 112 } 113 114 /** 115 * Register itself at the given event source. 116 * @param eventSource The emitter of the mouse events. 117 */ 118 public void register(NavigatableComponent eventSource) { 119 eventSource.addMouseListener(this); 120 eventSource.addMouseMotionListener(this); 121 selectionEndedListener.addPropertyChangeListener(this); 122 122 eventSource.addPropertyChangeListener("scale", new PropertyChangeListener(){ 123 124 125 126 127 123 public void propertyChange(PropertyChangeEvent evt) { 124 if (mousePosStart != null) { 125 paintRect(); 126 mousePos = mousePosStart = null; 127 } 128 128 } 129 129 }); 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 int buttonPressed = e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK); 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 *that are touched, instead those which are completly covered.267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 130 } 131 /** 132 * Unregister itself from the given event source. If a selection rectangle is 133 * shown, hide it first. 134 * 135 * @param eventSource The emitter of the mouse events. 136 */ 137 public void unregister(Component eventSource) { 138 eventSource.removeMouseListener(this); 139 eventSource.removeMouseMotionListener(this); 140 selectionEndedListener.removePropertyChangeListener(this); 141 } 142 143 /** 144 * If the correct button, from the "drawing rectangle" mode 145 */ 146 public void mousePressed(MouseEvent e) { 147 if (e.getButton() == MouseEvent.BUTTON1) 148 mousePosStart = mousePos = e.getPoint(); 149 } 150 151 /** 152 * If the correct button is hold, draw the rectangle. 153 */ 154 public void mouseDragged(MouseEvent e) { 155 int buttonPressed = e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK); 156 157 158 if (buttonPressed != 0) { 159 if (mousePosStart == null) 160 mousePosStart = mousePos = e.getPoint(); 161 paintRect(); 162 } 163 164 if (buttonPressed == MouseEvent.BUTTON1_DOWN_MASK) { 165 mousePos = e.getPoint(); 166 paintRect(); 167 } else if (buttonPressed == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) { 168 mousePosStart.x += e.getX()-mousePos.x; 169 mousePosStart.y += e.getY()-mousePos.y; 170 mousePos = e.getPoint(); 171 paintRect(); 172 } 173 } 174 175 /** 176 * Check the state of the keys and buttons and set the selection accordingly. 177 */ 178 public void mouseReleased(MouseEvent e) { 179 if (e.getButton() != MouseEvent.BUTTON1) 180 return; 181 if (mousePos == null || mousePosStart == null) 182 return; // injected release from outside 183 184 // disable the selection rect 185 paintRect(); 186 Rectangle r = getSelectionRectangle(); 187 mousePosStart = null; 188 mousePos = null; 189 190 boolean shift = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0; 191 boolean alt = (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0; 192 boolean ctrl = (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0; 193 if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0) 194 selectionEndedListener.selectionEnded(r, alt, shift, ctrl); 195 } 196 197 198 /** 199 * Draw a selection rectangle on screen. If already a rectangle is drawn, 200 * it is removed instead. 201 */ 202 private void paintRect() { 203 if (mousePos == null || mousePosStart == null || mousePos == mousePosStart) 204 return; 205 Graphics g = nc.getGraphics(); 206 g.setColor(Color.BLACK); 207 g.setXORMode(Color.WHITE); 208 209 Rectangle r = getSelectionRectangle(); 210 g.drawRect(r.x,r.y,r.width,r.height); 211 } 212 213 /** 214 * Calculate and return the current selection rectangle 215 * @return A rectangle that spans from mousePos to mouseStartPos 216 */ 217 private Rectangle getSelectionRectangle() { 218 int x = mousePosStart.x; 219 int y = mousePosStart.y; 220 int w = mousePos.x - mousePosStart.x; 221 int h = mousePos.y - mousePosStart.y; 222 if (w < 0) { 223 x += w; 224 w = -w; 225 } 226 if (h < 0) { 227 y += h; 228 h = -h; 229 } 230 231 if (aspectRatio) { 232 /* Keep the aspect ratio by growing the rectangle; the 233 * rectangle is always under the cursor. */ 234 double aspectRatio = (double)nc.getWidth()/nc.getHeight(); 235 if ((double)w/h < aspectRatio) { 236 int neww = (int)(h*aspectRatio); 237 if (mousePos.x < mousePosStart.x) 238 x += w - neww; 239 w = neww; 240 } else { 241 int newh = (int)(w/aspectRatio); 242 if (mousePos.y < mousePosStart.y) 243 y += h - newh; 244 h = newh; 245 } 246 } 247 248 return new Rectangle(x,y,w,h); 249 } 250 251 /** 252 * If the action goes inactive, remove the selection rectangle from screen 253 */ 254 public void propertyChange(PropertyChangeEvent evt) { 255 if (evt.getPropertyName().equals("active") && !(Boolean)evt.getNewValue() && mousePosStart != null) { 256 paintRect(); 257 mousePosStart = null; 258 mousePos = null; 259 } 260 } 261 262 /** 263 * Return a list of all objects in the rectangle, respecting the different 264 * modifier. 265 * @param alt Whether the alt key was pressed, which means select all objects 266 * that are touched, instead those which are completly covered. 267 */ 268 public Collection<OsmPrimitive> getObjectsInRectangle(Rectangle r, boolean alt) { 269 Collection<OsmPrimitive> selection = new LinkedList<OsmPrimitive>(); 270 271 // whether user only clicked, not dragged. 272 boolean clicked = r.width <= 2 && r.height <= 2; 273 Point center = new Point(r.x+r.width/2, r.y+r.height/2); 274 275 if (clicked) { 276 OsmPrimitive osm = nc.getNearest(center); 277 if (osm != null) 278 selection.add(osm); 279 } else { 280 // nodes 281 for (Node n : nc.getData().nodes) { 282 if (!n.deleted && !n.incomplete && r.contains(nc.getPoint(n.eastNorth))) 283 selection.add(n); 284 } 285 286 // ways 287 for (Way w : nc.getData().ways) { 288 if (w.deleted || w.nodes.isEmpty() || w.incomplete) 289 continue; 290 if (alt) { 291 for (Node n : w.nodes) { 292 if (!n.incomplete && r.contains(nc.getPoint(n.eastNorth))) { 293 selection.add(w); 294 break; 295 } 296 } 297 } else { 298 boolean allIn = true; 299 for (Node n : w.nodes) { 300 if (!n.incomplete && !r.contains(nc.getPoint(n.eastNorth))) { 301 allIn = false; 302 break; 303 } 304 } 305 if (allIn) selection.add(w); 306 } 307 } 308 } 309 return selection; 310 } 311 312 public void mouseClicked(MouseEvent e) {} 313 public void mouseEntered(MouseEvent e) {} 314 public void mouseExited(MouseEvent e) {} 315 public void mouseMoved(MouseEvent e) {} 316 316 } -
trunk/src/org/openstreetmap/josm/gui/SideButton.java
r1103 r1169 12 12 13 13 public class SideButton extends JButton { 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 14 public SideButton(Action action) 15 { 16 super(action); 17 doStyle(); 18 setText(null); 19 } 20 public SideButton(String imagename, String property, String tooltip, ActionListener actionListener) 21 { 22 super(ImageProvider.get("dialogs", imagename)); 23 doStyle(); 24 setActionCommand(imagename); 25 addActionListener(actionListener); 26 setToolTipText(tooltip); 27 } 28 @Deprecated 29 public SideButton(String name, String imagename, String property, String tooltip, int mnemonic, ActionListener actionListener) 30 { 31 super(tr(name), ImageProvider.get("dialogs", imagename)); 32 setMnemonic(mnemonic); 33 setup(name, property, tooltip, actionListener); 34 } 35 public SideButton(String name, String imagename, String property, String tooltip, Shortcut shortcut, ActionListener actionListener) 36 { 37 super(tr(name), ImageProvider.get("dialogs", imagename)); 38 if(shortcut != null) 39 39 { 40 40 shortcut.setMnemonic(this); 41 41 if(tooltip != null) 42 42 tooltip = Main.platform.makeTooltip(tooltip, shortcut); 43 43 } 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 44 setup(name, property, tooltip, actionListener); 45 } 46 public SideButton(String name, String imagename, String property, String tooltip, ActionListener actionListener) 47 { 48 super(tr(name), ImageProvider.get("dialogs", imagename)); 49 setup(name, property, tooltip, actionListener); 50 } 51 private void setup(String name, String property, String tooltip, ActionListener actionListener) 52 { 53 doStyle(); 54 setActionCommand(name); 55 addActionListener(actionListener); 56 setToolTipText(tooltip); 57 putClientProperty("help", "Dialog/"+property+"/"+name); 58 } 59 private void doStyle() 60 { 61 setMargin(new Insets(1,1,1,1)); 62 setIconTextGap(2); 63 } 64 64 } -
trunk/src/org/openstreetmap/josm/gui/SplashScreen.java
r1138 r1169 30 30 /** 31 31 * Show a splash screen so the user knows what is happening during startup. 32 * 33 * @author cbrill 32 * 33 * @author cbrill 34 34 */ 35 35 public class SplashScreen extends JWindow { 36 36 37 38 37 private JLabel status; 38 private boolean visible; 39 39 40 40 private Runnable closerRunner; 41 41 42 43 44 42 public SplashScreen(boolean visible) { 43 super(); 44 this.visible=visible; 45 45 46 47 46 if (!visible) 47 return; 48 48 49 50 51 52 49 // Add a nice border to the main splash screen 50 JPanel contentPane = (JPanel)this.getContentPane(); 51 Border margin = new EtchedBorder(1, Color.white, Color.gray); 52 contentPane.setBorder(margin); 53 53 54 55 56 57 58 54 // Add a margin from the border to the content 55 JPanel innerContentPane = new JPanel(); 56 innerContentPane.setBorder(new EmptyBorder(10, 10, 2, 10)); 57 contentPane.add(innerContentPane); 58 innerContentPane.setLayout(new GridBagLayout()); 59 59 60 61 62 63 64 60 // Add the logo 61 JLabel logo = new JLabel(ImageProvider.get("logo.png")); 62 GridBagConstraints gbc = new GridBagConstraints(); 63 gbc.gridheight = 2; 64 innerContentPane.add(logo, gbc); 65 65 66 67 68 69 70 71 72 66 // Add the name of this application 67 JLabel caption = new JLabel(tr("JOSM - Java OpenStreetMap Editor")); 68 caption.setFont(new Font("Helvetica", Font.BOLD, 20)); 69 gbc.gridheight = 1; 70 gbc.gridx = 1; 71 gbc.insets = new Insets(30, 0, 0, 0); 72 innerContentPane.add(caption, gbc); 73 73 74 75 76 77 78 74 // Add the version number 75 JLabel version = new JLabel(tr("Version {0}", AboutAction.getVersionString())); 76 gbc.gridy = 1; 77 gbc.insets = new Insets(0, 0, 0, 0); 78 innerContentPane.add(version, gbc); 79 79 80 81 82 83 84 85 86 87 80 // Add a separator to the status text 81 JSeparator separator = new JSeparator(JSeparator.HORIZONTAL); 82 gbc.gridx = 0; 83 gbc.gridy = 2; 84 gbc.gridwidth = 2; 85 gbc.fill = GridBagConstraints.HORIZONTAL; 86 gbc.insets = new Insets(15, 0, 5, 0); 87 innerContentPane.add(separator, gbc); 88 88 89 90 91 92 93 94 89 // Add a status message 90 status = new JLabel(); 91 gbc.gridy = 3; 92 gbc.insets = new Insets(0, 0, 0, 0); 93 innerContentPane.add(status, gbc); 94 setStatus(tr("Initializing")); 95 95 96 96 pack(); 97 97 98 99 100 101 102 98 // Center the splash screen 99 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 100 Dimension labelSize = contentPane.getPreferredSize(); 101 setLocation(screenSize.width / 2 - (labelSize.width / 2), 102 screenSize.height / 2 - (labelSize.height / 2)); 103 103 104 105 106 107 108 109 110 104 // Method to close the splash screen when being clicked or when closeSplash is called 105 closerRunner = new Runnable() { 106 public void run() { 107 setVisible(false); 108 dispose(); 109 } 110 }; 111 111 112 113 114 115 116 117 118 119 120 121 122 123 124 112 // Add ability to hide splash screen by clicking it 113 addMouseListener(new MouseAdapter() { 114 public void mousePressed(MouseEvent event) { 115 try { 116 closerRunner.run(); 117 } catch (Exception e) { 118 e.printStackTrace(); 119 // can catch InvocationTargetException 120 // can catch InterruptedException 121 } 122 } 123 }); 124 125 125 // Hide splashscreen when other window is created 126 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.WINDOW_EVENT_MASK); 127 128 setVisible(true); 129 } 130 131 private AWTEventListener awtListener = new AWTEventListener() { 132 public void eventDispatched(AWTEvent event) { 133 if (event.getSource() != SplashScreen.this) { 134 closeSplash(); 135 } 136 } 137 }; 126 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.WINDOW_EVENT_MASK); 138 127 139 /** 140 * This method sets the status message. It should be called prior to 141 * actually doing the action. 142 * 143 * @param message 144 * the message to be displayed 145 */ 146 public void setStatus(String message) { 147 if (!visible) 148 return; 149 status.setText(message + " ..."); 150 } 128 setVisible(true); 129 } 151 130 152 /** 153 * Closes the splashscreen. Call once you are done starting. 154 */ 155 public void closeSplash() { 156 if (!visible) 157 return; 158 Toolkit.getDefaultToolkit().removeAWTEventListener(awtListener); 159 try { 160 SwingUtilities.invokeLater(closerRunner); 161 } catch (Exception e) { 162 e.printStackTrace(); 163 // can catch InvocationTargetException 164 // can catch InterruptedException 165 } 166 } 131 private AWTEventListener awtListener = new AWTEventListener() { 132 public void eventDispatched(AWTEvent event) { 133 if (event.getSource() != SplashScreen.this) { 134 closeSplash(); 135 } 136 } 137 }; 138 139 /** 140 * This method sets the status message. It should be called prior to 141 * actually doing the action. 142 * 143 * @param message 144 * the message to be displayed 145 */ 146 public void setStatus(String message) { 147 if (!visible) 148 return; 149 status.setText(message + " ..."); 150 } 151 152 /** 153 * Closes the splashscreen. Call once you are done starting. 154 */ 155 public void closeSplash() { 156 if (!visible) 157 return; 158 Toolkit.getDefaultToolkit().removeAWTEventListener(awtListener); 159 try { 160 SwingUtilities.invokeLater(closerRunner); 161 } catch (Exception e) { 162 e.printStackTrace(); 163 // can catch InvocationTargetException 164 // can catch InterruptedException 165 } 166 } 167 167 168 168 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java
r1084 r1169 24 24 public class CommandStackDialog extends ToggleDialog implements CommandQueueListener { 25 25 26 26 private DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode()); 27 27 private JTree tree = new JTree(treeModel); 28 28 29 30 31 32 29 public CommandStackDialog(final MapFrame mapFrame) { 30 super(tr("Command Stack"), "commandstack", tr("Open a list of all commands (undo buffer)."), 31 Shortcut.registerShortcut("subwindow:commandstack", tr("Toggle: {0}", tr("Command Stack")), KeyEvent.VK_O, Shortcut.GROUP_LAYER), 100); 32 Main.main.undoRedo.listenerCommands.add(this); 33 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 34 tree.setRootVisible(false); 35 tree.setShowsRootHandles(true); 36 tree.expandRow(0); 37 tree.setCellRenderer(new DefaultTreeCellRenderer(){ 38 @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 39 super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); 40 DefaultMutableTreeNode v = (DefaultMutableTreeNode)value; 41 if (v.getUserObject() instanceof JLabel) { 42 JLabel l = (JLabel)v.getUserObject(); 43 setIcon(l.getIcon()); 44 setText(l.getText()); 45 } 46 return this; 47 } 48 }); 49 tree.setVisibleRowCount(8); 50 add(new JScrollPane(tree), BorderLayout.CENTER); 51 } 52 52 53 54 55 56 57 58 59 53 @Override public void setVisible(boolean v) { 54 if (v) 55 buildList(); 56 else if (tree != null) 57 treeModel.setRoot(new DefaultMutableTreeNode()); 58 super.setVisible(v); 59 } 60 60 61 62 63 64 65 66 67 68 69 70 61 private void buildList() { 62 if (Main.map == null || Main.map.mapView == null || Main.map.mapView.editLayer == null) 63 return; 64 Collection<Command> commands = Main.main.undoRedo.commands; 65 DefaultMutableTreeNode root = new DefaultMutableTreeNode(); 66 for (Command c : commands) 67 root.add(c.description()); 68 treeModel.setRoot(root); 69 tree.scrollRowToVisible(treeModel.getChildCount(root)-1); 70 } 71 71 72 73 74 72 public void commandChanged(int queueSize, int redoSize) { 73 if (!isVisible()) 74 return; 75 75 treeModel.setRoot(new DefaultMutableTreeNode()); 76 76 buildList(); 77 77 } 78 78 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java
r1117 r1169 47 47 public final class ConflictDialog extends ToggleDialog { 48 48 49 50 51 49 public final Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>(); 50 private final DefaultListModel model = new DefaultListModel(); 51 private final JList displaylist = new JList(model); 52 52 53 54 55 56 57 58 59 60 61 62 63 64 53 public ConflictDialog() { 54 super(tr("Conflict"), "conflict", tr("Merging conflicts."), 55 Shortcut.registerShortcut("subwindow:conflict", tr("Toggle: {0}", tr("Conflict")), KeyEvent.VK_C, Shortcut.GROUP_LAYER), 100); 56 displaylist.setCellRenderer(new OsmPrimitivRenderer()); 57 displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 58 displaylist.addMouseListener(new MouseAdapter(){ 59 @Override public void mouseClicked(MouseEvent e) { 60 if (e.getClickCount() >= 2) 61 resolve(); 62 } 63 }); 64 add(new JScrollPane(displaylist), BorderLayout.CENTER); 65 65 66 67 68 69 70 71 72 66 JPanel buttonPanel = new JPanel(new GridLayout(1,2)); 67 buttonPanel.add(new SideButton(marktr("Resolve"), "conflict", "Conflict", 68 tr("Open a merge dialog of all selected items in the list above."), new ActionListener(){ 69 public void actionPerformed(ActionEvent e) { 70 resolve(); 71 } 72 })); 73 73 74 75 76 77 78 79 80 81 82 83 74 buttonPanel.add(new SideButton(marktr("Select"), "select", "Conflict", 75 tr("Set the selected elements on the map to the selected items in the list above."), new ActionListener(){ 76 public void actionPerformed(ActionEvent e) { 77 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>(); 78 for (Object o : displaylist.getSelectedValues()) 79 sel.add((OsmPrimitive)o); 80 Main.ds.setSelected(sel); 81 } 82 })); 83 add(buttonPanel, BorderLayout.SOUTH); 84 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 85 DataSet.selListeners.add(new SelectionChangedListener(){ 86 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 87 displaylist.clearSelection(); 88 for (OsmPrimitive osm : newSelection) { 89 if (conflicts.containsKey(osm)) { 90 int pos = model.indexOf(osm); 91 displaylist.addSelectionInterval(pos, pos); 92 } 93 } 94 } 95 }); 96 displaylist.getSelectionModel().addListSelectionListener(new ListSelectionListener(){ 97 public void valueChanged(ListSelectionEvent e) { 98 Main.map.mapView.repaint(); 99 } 100 }); 101 } 102 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 103 private final void resolve() { 104 if (displaylist.getSelectedIndex() == -1) { 105 JOptionPane.showMessageDialog(Main.parent,tr("Please select something from the conflict list.")); 106 return; 107 } 108 Map<OsmPrimitive, OsmPrimitive> sel = new HashMap<OsmPrimitive, OsmPrimitive>(); 109 for (int i : displaylist.getSelectedIndices()) { 110 OsmPrimitive s = (OsmPrimitive)model.get(i); 111 sel.put(s, conflicts.get(s)); 112 } 113 ConflictResolver resolver = new ConflictResolver(sel); 114 int answer = JOptionPane.showConfirmDialog(Main.parent, resolver, tr("Resolve Conflicts"), JOptionPane.OK_CANCEL_OPTION); 115 if (answer != JOptionPane.OK_OPTION) 116 return; 117 Main.main.undoRedo.add(new ConflictResolveCommand(resolver.conflicts, sel)); 118 Main.map.mapView.repaint(); 119 } 120 120 121 122 123 124 125 126 127 128 121 public final void rebuildList() { 122 model.removeAllElements(); 123 for (OsmPrimitive osm : this.conflicts.keySet()) 124 if (osm instanceof Node) 125 model.addElement(osm); 126 for (OsmPrimitive osm : this.conflicts.keySet()) 127 if (osm instanceof Way) 128 model.addElement(osm); 129 129 for (OsmPrimitive osm : this.conflicts.keySet()) 130 130 if (osm instanceof Relation) … … 132 132 } 133 133 134 135 136 137 134 public final void add(Map<OsmPrimitive, OsmPrimitive> conflicts) { 135 this.conflicts.putAll(conflicts); 136 rebuildList(); 137 } 138 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 139 /** 140 * Paint all conflicts that can be expressed on the main window. 141 */ 142 public void paintConflicts(final Graphics g, final NavigatableComponent nc) { 143 Color preferencesColor = Main.pref.getColor("conflict", Color.gray); 144 if (preferencesColor.equals(Color.BLACK)) 145 return; 146 g.setColor(preferencesColor); 147 Visitor conflictPainter = new Visitor(){ 148 public void visit(Node n) { 149 Point p = nc.getPoint(n.eastNorth); 150 g.drawRect(p.x-1, p.y-1, 2, 2); 151 } 152 public void visit(Node n1, Node n2) { 153 Point p1 = nc.getPoint(n1.eastNorth); 154 Point p2 = nc.getPoint(n2.eastNorth); 155 g.drawLine(p1.x, p1.y, p2.x, p2.y); 156 } 157 public void visit(Way w) { 158 Node lastN = null; 159 for (Node n : w.nodes) { 160 if (lastN == null) { 161 lastN = n; 162 continue; 163 } 164 visit(lastN, n); 165 lastN = n; 166 } 167 } 168 public void visit(Relation e) { 169 for (RelationMember em : e.members) 170 em.member.visit(this); 171 } 172 }; 173 for (Object o : displaylist.getSelectedValues()) 174 conflicts.get(o).visit(conflictPainter); 175 } 176 176 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java
r1084 r1169 54 54 public class HistoryDialog extends ToggleDialog implements SelectionChangedListener { 55 55 56 57 58 59 60 61 62 56 public static final Date unifyDate(Date d) { 57 Calendar c = Calendar.getInstance(); 58 c.setTime(d); 59 c.set(Calendar.MINUTE, 0); 60 c.set(Calendar.SECOND, 0); 61 return c.getTime(); 62 } 63 63 64 65 66 64 private static class HistoryItem implements Comparable<HistoryItem> { 65 OsmPrimitive osm; 66 boolean visible; 67 67 68 69 70 71 68 public int compareTo(HistoryItem o) { 69 return unifyDate(osm.getTimestamp()).compareTo(unifyDate(o.osm.getTimestamp())); 70 } 71 } 72 72 73 74 75 76 77 73 private final DefaultTableModel data = new DefaultTableModel(){ 74 @Override public boolean isCellEditable(int row, int column) { 75 return false; 76 } 77 }; 78 78 79 80 81 82 83 84 79 /** 80 * Main table. 3 columns: 81 * Object | Date | visible (icon, no text) 82 */ 83 private JTable history = new JTable(data); 84 private JScrollPane historyPane = new JScrollPane(history); 85 85 86 87 86 private Map<OsmPrimitive, List<HistoryItem>> cache = new HashMap<OsmPrimitive, List<HistoryItem>>(); 87 private JLabel notLoaded = new JLabel("<html><i>"+tr("Click Reload to refresh list")+"</i></html>"); 88 88 89 90 91 92 93 94 95 89 public HistoryDialog() { 90 super(tr("History"), "history", tr("Display the history of all selected items."), 91 Shortcut.registerShortcut("subwindow:history", tr("Toggle: {0}", tr("History")), KeyEvent.VK_H, 92 Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150); 93 historyPane.setVisible(false); 94 notLoaded.setVisible(true); 95 notLoaded.setHorizontalAlignment(JLabel.CENTER); 96 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 97 history.setDefaultRenderer(Object.class, new DefaultTableCellRenderer(){ 98 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 99 return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 100 } 101 }); 102 data.setColumnIdentifiers(new Object[]{tr("Object"),tr("Date"),""}); 103 history.getColumnModel().getColumn(0).setPreferredWidth(200); 104 history.getColumnModel().getColumn(1).setPreferredWidth(200); 105 history.getColumnModel().getColumn(2).setPreferredWidth(20); 106 final TableCellRenderer oldRenderer = history.getTableHeader().getDefaultRenderer(); 107 history.getTableHeader().setDefaultRenderer(new DefaultTableCellRenderer(){ 108 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 109 JComponent c = (JComponent)oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 110 if (!value.equals("")) 111 return c; 112 JLabel l = new JLabel(ImageProvider.get("misc","showhide")); 113 l.setForeground(c.getForeground()); 114 l.setBackground(c.getBackground()); 115 l.setFont(c.getFont()); 116 l.setBorder(c.getBorder()); 117 l.setOpaque(true); 118 return l; 119 } 120 }); 121 121 122 123 124 125 122 JPanel centerPanel = new JPanel(new GridBagLayout()); 123 centerPanel.add(notLoaded, GBC.eol().fill(GBC.BOTH)); 124 centerPanel.add(historyPane, GBC.eol().fill(GBC.BOTH)); 125 add(centerPanel, BorderLayout.CENTER); 126 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 127 JPanel buttons = new JPanel(new GridLayout(1,2)); 128 buttons.add(new SideButton(marktr("Reload"), "refresh", "History", tr("Reload all currently selected objects and refresh the list."), 129 new ActionListener(){ 130 public void actionPerformed(ActionEvent e) { 131 reload(); 132 } 133 })); 134 buttons.add(new SideButton(marktr("Revert"), "revert", "History", 135 tr("Revert the state of all currently selected objects to the version selected in the history list."), new ActionListener(){ 136 public void actionPerformed(ActionEvent e) { 137 JOptionPane.showMessageDialog(Main.parent, tr("Not implemented yet.")); 138 } 139 })); 140 add(buttons, BorderLayout.SOUTH); 141 141 142 143 142 DataSet.selListeners.add(this); 143 } 144 144 145 145 146 147 148 149 150 146 @Override public void setVisible(boolean b) { 147 super.setVisible(b); 148 if (b) 149 update(); 150 } 151 151 152 152 153 154 155 156 153 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 154 if (isVisible()) 155 update(); 156 } 157 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 158 /** 159 * Identify all new objects in the selection and if any, hide the list. 160 * Else, update the list with the selected items shown. 161 */ 162 private void update() { 163 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 164 if (!cache.keySet().containsAll(sel)) { 165 historyPane.setVisible(false); 166 notLoaded.setVisible(true); 167 } else { 168 SortedSet<HistoryItem> orderedHistory = new TreeSet<HistoryItem>(); 169 for (OsmPrimitive osm : sel) 170 orderedHistory.addAll(cache.get(osm)); 171 data.setRowCount(0); 172 for (HistoryItem i : orderedHistory) 173 data.addRow(new Object[]{i.osm, i.osm.timestamp, i.visible}); 174 historyPane.setVisible(true); 175 notLoaded.setVisible(false); 176 } 177 } 178 178 179 180 181 179 void reload() { 180 JOptionPane.showMessageDialog(Main.parent, tr("Not implemented yet.")); 181 } 182 182 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
r1084 r1169 50 50 public class LayerListDialog extends ToggleDialog implements LayerChangeListener { 51 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 52 /** 53 * The last layerlist created. Used to update the list in the Show/Hide and Delete actions. 54 * TODO: Replace with Listener-Pattern. 55 */ 56 static JList instance; 57 private JScrollPane listScrollPane; 58 59 public final static class DeleteLayerAction extends AbstractAction { 60 61 private final Layer layer; 62 63 public DeleteLayerAction(Layer layer) { 64 super(tr("Delete"), ImageProvider.get("dialogs", "delete")); 65 putValue(SHORT_DESCRIPTION, tr("Delete the selected layer.")); 66 putValue("help", "Action/LayerDelete"); 67 this.layer = layer; 68 } 69 70 public void actionPerformed(ActionEvent e) { 71 int sel = instance.getSelectedIndex(); 72 Layer l = layer != null ? layer : (Layer)instance.getSelectedValue(); 73 if(l == null) 74 return; 75 if (l instanceof OsmDataLayer) 76 { 77 if (((OsmDataLayer)l).isModified()) 78 { 79 if(JOptionPane.showConfirmDialog(instance, tr("There are unsaved changes. Delete the layer anwyay?"), 80 tr("Unsaved Changes"), JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) 81 return; 82 } 83 else if(!DontShowAgainInfo.show("delete_layer", tr("Do you really want to delete the whole layer?"), false)) 84 return; 85 } 86 Main.main.removeLayer(l); 87 if (sel >= instance.getModel().getSize()) 88 sel = instance.getModel().getSize()-1; 89 if (instance.getSelectedValue() == null) 90 instance.setSelectedIndex(sel); 91 if (Main.map != null) 92 Main.map.mapView.setActiveLayer((Layer)instance.getSelectedValue()); 93 } 94 } 95 96 public final static class ShowHideLayerAction extends AbstractAction { 97 private final Layer layer; 98 99 public ShowHideLayerAction(Layer layer) { 100 super(tr("Show/Hide"), ImageProvider.get("dialogs", "showhide")); 101 putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer.")); 102 putValue("help", "Action/LayerShowHide"); 103 this.layer = layer; 104 } 105 106 public void actionPerformed(ActionEvent e) { 107 Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer; 108 if(l == null) 109 return; 110 l.visible = !l.visible; 111 Main.map.mapView.repaint(); 112 instance.repaint(); 113 } 114 } 115 116 public final static class ShowHideMarkerText extends AbstractAction { 117 private final Layer layer; 118 119 public ShowHideMarkerText(Layer layer) { 120 super(tr("Show/Hide Text/Icons"), ImageProvider.get("dialogs", "showhide")); 121 putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the marker text and icons.")); 122 putValue("help", "Action/ShowHideTextIcons"); 123 this.layer = layer; 124 } 125 126 public void actionPerformed(ActionEvent e) { 127 Layer l = layer == null ? (Layer)instance.getSelectedValue() : layer; 128 String current = Main.pref.get("marker.show "+l.name,"show"); 129 Main.pref.put("marker.show "+l.name, current.equalsIgnoreCase("show") ? "hide" : "show"); 130 Main.map.mapView.repaint(); 131 instance.repaint(); 132 } 133 } 134 135 /** 136 * The data model for the list component. 137 */ 138 DefaultListModel model = new DefaultListModel(); 139 /** 140 * The merge action. This is only called, if the current selection and its 141 * item below are editable datasets and the merge button is clicked. 142 */ 143 private final SideButton mergeButton; 144 /** 145 * Button for moving layer up. 146 */ 147 private final SideButton upButton; 148 /** 149 * Button for moving layer down. 150 */ 151 private final SideButton downButton; 152 /** 153 * Button for delete layer. 154 */ 155 private Action deleteAction = new DeleteLayerAction(null); 156 157 /** 158 * Create an layerlist and attach it to the given mapView. 159 */ 160 public LayerListDialog(MapFrame mapFrame) { 161 super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."), 162 Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100); 163 instance = new JList(model); 164 listScrollPane = new JScrollPane(instance); 165 add(listScrollPane, BorderLayout.CENTER); 166 instance.setBackground(UIManager.getColor("Button.background")); 167 instance.setCellRenderer(new DefaultListCellRenderer(){ 168 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 169 Layer layer = (Layer)value; 170 JLabel label = (JLabel)super.getListCellRendererComponent(list, 171 layer.name, index, isSelected, cellHasFocus); 172 Icon icon = layer.getIcon(); 173 if (!layer.visible) 174 icon = ImageProvider.overlay(icon, "overlay/invisible", OverlayPosition.SOUTHEAST); 175 label.setIcon(icon); 176 label.setToolTipText(layer.getToolTipText()); 177 return label; 178 } 179 }); 180 181 final MapView mapView = mapFrame.mapView; 182 183 Collection<Layer> data = mapView.getAllLayers(); 184 for (Layer l : data) 185 model.addElement(l); 186 187 instance.setSelectedValue(mapView.getActiveLayer(), true); 188 instance.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 189 instance.addListSelectionListener(new ListSelectionListener(){ 190 public void valueChanged(ListSelectionEvent e) { 191 if (instance.getModel().getSize() == 0) 192 return; 193 if (instance.getSelectedIndex() == -1) 194 instance.setSelectedIndex(e.getFirstIndex()); 195 mapView.setActiveLayer((Layer)instance.getSelectedValue()); 196 } 197 }); 198 Layer.listeners.add(this); 199 200 instance.addMouseListener(new MouseAdapter(){ 201 private void openPopup(MouseEvent e) { 202 Point p = listScrollPane.getMousePosition(); 203 if (p == null) 204 return; // user is faster than swing with mouse movement 205 int index = instance.locationToIndex(e.getPoint()); 206 Layer layer = (Layer)instance.getModel().getElementAt(index); 207 LayerListPopup menu = new LayerListPopup(instance, layer); 208 menu.show(listScrollPane, p.x, p.y-3); 209 } 210 @Override public void mousePressed(MouseEvent e) { 211 if (e.isPopupTrigger()) 212 openPopup(e); 213 } 214 @Override public void mouseReleased(MouseEvent e) { 215 if (e.isPopupTrigger()) 216 openPopup(e); 217 } 218 @Override public void mouseClicked(MouseEvent e) { 219 if (e.getClickCount() == 2) { 220 int index = instance.locationToIndex(e.getPoint()); 221 Layer layer = (Layer)instance.getModel().getElementAt(index); 222 String current = Main.pref.get("marker.show "+layer.name,"show"); 223 Main.pref.put("marker.show "+layer.name, current.equalsIgnoreCase("show") ? "hide" : "show"); 224 layer.visible = !layer.visible; 225 Main.map.mapView.repaint(); 226 instance.repaint(); 227 } 228 } 229 }); 230 231 232 // Buttons 233 JPanel buttonPanel = new JPanel(new GridLayout(1, 5)); 234 235 ActionListener upDown = new ActionListener(){ 236 public void actionPerformed(ActionEvent e) { 237 Layer l = (Layer)instance.getSelectedValue(); 238 int sel = instance.getSelectedIndex(); 239 int selDest = e.getActionCommand().equals("up") ? sel-1 : sel+1; 240 mapView.moveLayer(l, selDest); 241 model.set(sel, model.get(selDest)); 242 model.set(selDest, l); 243 instance.setSelectedIndex(selDest); 244 updateButtonEnabled(); 245 mapView.repaint(); 246 } 247 }; 248 249 upButton = new SideButton("up", "LayerList", tr("Move the selected layer one row up."), upDown); 250 buttonPanel.add(upButton); 251 252 downButton = new SideButton("down", "LayerList", tr("Move the selected layer one row down."), upDown); 253 buttonPanel.add(downButton); 254 255 buttonPanel.add(new SideButton(new ShowHideLayerAction(null))); 256 buttonPanel.add(new SideButton(deleteAction)); 257 258 mergeButton = new SideButton("Merge", "mergedown", "LayerList", tr("Merge the layer directly below into the selected layer."), 259 new ActionListener(){ 260 public void actionPerformed(ActionEvent e) { 261 Layer lTo = (Layer)instance.getSelectedValue(); 262 Layer lFrom = (Layer)model.get(instance.getSelectedIndex()+1); 263 lTo.mergeFrom(lFrom); 264 mapView.removeLayer(lFrom); 265 updateButtonEnabled(); 266 mapView.repaint(); 267 } 268 }); 269 mergeButton.setText(null); 270 buttonPanel.add(mergeButton); 271 272 add(buttonPanel, BorderLayout.SOUTH); 273 274 updateButtonEnabled(); 275 } 276 277 /** 278 * Updates the state of the Buttons. 279 */ 280 void updateButtonEnabled() { 281 int sel = instance.getSelectedIndex(); 282 Layer l = (Layer)instance.getSelectedValue(); 283 boolean enable = model.getSize() > 1; 284 enable = enable && sel < model.getSize()-1; 285 enable = enable && ((Layer)model.get(sel+1)).isMergable(l); 286 mergeButton.setEnabled(enable); 287 upButton.setEnabled(sel > 0); 288 downButton.setEnabled(sel >= 0 && sel < model.getSize()-1); 289 deleteAction.setEnabled(!model.isEmpty()); 290 } 291 292 /** 293 * Add the new layer to the list. 294 */ 295 public void layerAdded(Layer newLayer) { 296 model.add(model.size(), newLayer); 297 updateButtonEnabled(); 298 } 299 300 public void layerRemoved(Layer oldLayer) { 301 model.removeElement(oldLayer); 302 if (model.isEmpty()) { 303 Layer.listeners.remove(this); 304 return; 305 } 306 if (instance.getSelectedIndex() == -1) 307 instance.setSelectedIndex(0); 308 updateButtonEnabled(); 309 } 310 311 /** 312 * If the newLayer is not the actual selection, select it. 313 */ 314 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 315 if (newLayer != instance.getSelectedValue()) 316 instance.setSelectedValue(newLayer, true); 317 updateButtonEnabled(); 318 } 319 319 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListPopup.java
r627 r1169 21 21 public class LayerListPopup extends JPopupMenu { 22 22 23 24 25 26 27 28 29 30 31 23 public final static class InfoAction extends AbstractAction { 24 private final Layer layer; 25 public InfoAction(Layer layer) { 26 super(tr("Info"), ImageProvider.get("info")); 27 this.layer = layer; 28 } 29 public void actionPerformed(ActionEvent e) { 30 JOptionPane.showMessageDialog(Main.parent, layer.getInfoComponent()); 31 } 32 32 } 33 33 34 35 36 37 34 public LayerListPopup(final JList layers, final Layer layer) { 35 for (Component c : layer.getMenuEntries()) 36 add(c); 37 } 38 38 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/PropertiesDialog.java
r1168 r1169 85 85 public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener { 86 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 87 /** 88 * Used to display relation names in the membership table 89 */ 90 private NameVisitor nameVisitor = new NameVisitor(); 91 92 /** 93 * Watches for double clicks and from editing or new property, depending on the 94 * location, the click was. 95 * @author imi 96 */ 97 public class DblClickWatch extends MouseAdapter { 98 @Override public void mouseClicked(MouseEvent e) { 99 if (e.getClickCount() < 2) 100 { 101 if (e.getSource() == propertyTable) 102 membershipTable.clearSelection(); 103 else if (e.getSource() == membershipTable) 104 propertyTable.clearSelection(); 105 } 106 else if (e.getSource() == propertyTable) 107 { 108 int row = propertyTable.rowAtPoint(e.getPoint()); 109 if (row > -1) 110 propertyEdit(row); 111 } else if (e.getSource() == membershipTable) { 112 int row = membershipTable.rowAtPoint(e.getPoint()); 113 if (row > -1) 114 membershipEdit(row); 115 } 116 else 117 { 118 add(); 119 } 120 } 121 } 122 123 private final Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>(); 124 /** 125 * Edit the value in the properties table row 126 * @param row The row of the table from which the value is edited. 127 */ 128 void propertyEdit(int row) { 129 String key = propertyData.getValueAt(row, 0).toString(); 130 objKey=key; 131 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 132 if (sel.isEmpty()) { 133 JOptionPane.showMessageDialog(Main.parent, tr("Please select the objects you want to change properties for.")); 134 return; 135 } 136 String msg = "<html>"+trn("This will change up to {0} object.", "This will change up to {0} objects.", sel.size(), sel.size())+"<br><br>("+tr("An empty value deletes the key.", key)+")</html>"; 137 138 JPanel panel = new JPanel(new BorderLayout()); 139 panel.add(new JLabel(msg), BorderLayout.NORTH); 140 141 final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(true); 142 143 JPanel p = new JPanel(new GridBagLayout()); 144 panel.add(p, BorderLayout.CENTER); 145 146 final AutoCompleteComboBox keys = new AutoCompleteComboBox(); 147 keys.setPossibleItems(allData.keySet()); 148 keys.setEditable(true); 149 keys.setSelectedItem(key); 150 151 p.add(new JLabel(tr("Key")), GBC.std()); 152 p.add(Box.createHorizontalStrut(10), GBC.std()); 153 p.add(keys, GBC.eol().fill(GBC.HORIZONTAL)); 154 155 final AutoCompleteComboBox values = new AutoCompleteComboBox(); 156 values.setRenderer(new DefaultListCellRenderer() { 157 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 158 Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 159 if (c instanceof JLabel) { 160 String str = null; 161 str=(String) value; 162 if (valueCount.containsKey(objKey)){ 163 Map<String, Integer> m=valueCount.get(objKey); 164 if (m.containsKey(str)) { 165 str+="("+m.get(str)+")"; 166 c.setFont(c.getFont().deriveFont(Font.ITALIC+Font.BOLD)); 167 } 168 } 169 ((JLabel)c).setText(str); 170 } 171 return c; 172 } 173 }); 174 values.setEditable(true); 175 updateListData(key, allData, values); 176 Map<String, Integer> m=(Map<String, Integer>)propertyData.getValueAt(row, 1); 177 final String selection= m.size()!=1?tr("<different>"):m.entrySet().iterator().next().getKey(); 178 values.setSelectedItem(selection); 179 values.getEditor().setItem(selection); 180 p.add(new JLabel(tr("Value")), GBC.std()); 181 p.add(Box.createHorizontalStrut(10), GBC.std()); 182 p.add(values, GBC.eol().fill(GBC.HORIZONTAL)); 183 addFocusAdapter(row, allData, keys, values); 184 185 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) { 186 @Override public void selectInitialValue() { 187 values.requestFocusInWindow(); 188 values.getEditor().selectAll(); 189 } 190 }; 191 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Change values?")); 192 193 values.getEditor().addActionListener(new ActionListener() { 194 public void actionPerformed(ActionEvent e) { 195 dlg.setVisible(false); 196 optionPane.setValue(JOptionPane.OK_OPTION); 197 } 198 }); 199 200 String oldValue = values.getEditor().getItem().toString(); 201 dlg.setVisible(true); 202 203 Object answer = optionPane.getValue(); 204 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE || 205 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) { 206 values.getEditor().setItem(oldValue); 207 return; 208 } 209 210 String value = values.getEditor().getItem().toString(); 211 // is not Java 1.5 212 //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC); 213 if (value.equals("")) 214 value = null; // delete the key 215 String newkey = keys.getEditor().getItem().toString(); 216 //newkey = java.text.Normalizer.normalize(newkey, java.text.Normalizer.Form.NFC); 217 if (newkey.equals("")) { 218 newkey = key; 219 value = null; // delete the key instead 220 } 221 221 if (newkey.equals("created_by")) 222 222 { … … 224 224 return; 225 225 } 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 226 if (key.equals(newkey) || value == null) 227 Main.main.undoRedo.add(new ChangePropertyCommand(sel, newkey, value)); 228 else { 229 Collection<Command> commands=new Vector<Command>(); 230 commands.add(new ChangePropertyCommand(sel, key, null)); 231 if (value.equals(tr("<different>"))) { 232 HashMap<String, Vector<OsmPrimitive>> map=new HashMap<String, Vector<OsmPrimitive>>(); 233 for (OsmPrimitive osm: sel) { 234 if(osm.keys != null) 235 { 236 String val=osm.keys.get(key); 237 if(val != null) 238 { 239 if (map.containsKey(val)) { 240 map.get(val).add(osm); 241 } else { 242 Vector<OsmPrimitive> v = new Vector<OsmPrimitive>(); 243 v.add(osm); 244 map.put(val, v); 245 } 246 } 247 } 248 } 249 for (Entry<String, Vector<OsmPrimitive>> e: map.entrySet()) { 250 commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey())); 251 } 252 } else { 253 commands.add(new ChangePropertyCommand(sel, newkey, value)); 254 } 255 Main.main.undoRedo.add(new SequenceCommand(trn("Change properties of up to {0} object", "Change properties of up to {0} objects", sel.size(), sel.size()), commands)); 256 } 257 258 Main.ds.fireSelectionChanged(sel); 259 selectionChanged(sel); // update whole table 260 Main.parent.repaint(); // repaint all - drawing could have been changed 261 } 262 263 /** 264 264 * @param key 265 265 * @param allData … … 267 267 */ 268 268 private void updateListData(String key, final TreeMap<String, TreeSet<String>> allData, final AutoCompleteComboBox values) { 269 270 271 272 273 274 275 276 } 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 269 Collection<String> newItems; 270 if (allData.containsKey(key)) { 271 newItems = allData.get(key); 272 } else { 273 newItems = Collections.emptyList(); 274 } 275 values.setPossibleItems(newItems); 276 } 277 278 /** 279 * This simply fires up an relation editor for the relation shown; everything else 280 * is the editor's business. 281 * 282 * @param row 283 */ 284 void membershipEdit(int row) { 285 final RelationEditor editor = new RelationEditor((Relation)membershipData.getValueAt(row, 0), 286 (Collection<RelationMember>) membershipData.getValueAt(row, 1) ); 287 editor.setVisible(true); 288 } 289 290 /** 291 * Open the add selection dialog and add a new key/value to the table (and 292 * to the dataset, of course). 293 */ 294 void add() { 295 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 296 if (sel.isEmpty()) { 297 JOptionPane.showMessageDialog(Main.parent, tr("Please select objects for which you want to change properties.")); 298 return; 299 } 300 301 JPanel p = new JPanel(new BorderLayout()); 302 p.add(new JLabel("<html>"+trn("This will change up to {0} object.","This will change up to {0} objects.", sel.size(),sel.size())+"<br><br>"+tr("Please select a key")), 303 BorderLayout.NORTH); 304 final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(false); 305 final AutoCompleteComboBox keys = new AutoCompleteComboBox(); 306 keys.setPossibleItems(allData.keySet()); 307 keys.setEditable(true); 308 309 p.add(keys, BorderLayout.CENTER); 310 311 JPanel p2 = new JPanel(new BorderLayout()); 312 p.add(p2, BorderLayout.SOUTH); 313 p2.add(new JLabel(tr("Please select a value")), BorderLayout.NORTH); 314 final AutoCompleteComboBox values = new AutoCompleteComboBox(); 315 values.setEditable(true); 316 p2.add(values, BorderLayout.CENTER); 317 318 addFocusAdapter(-1, allData, keys, values); 319 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){ 320 @Override public void selectInitialValue() { 321 keys.requestFocusInWindow(); 322 keys.getEditor().selectAll(); 323 } 324 }; 325 pane.createDialog(Main.parent, tr("Change values?")).setVisible(true); 326 if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue())) 327 return; 328 String key = keys.getEditor().getItem().toString(); 329 String value = values.getEditor().getItem().toString(); 330 if (value.equals("")) 331 return; 332 332 if (key.equals("created_by")) 333 333 return; 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 * @param rowThe row, which key gets deleted from the dataset.387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 334 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, value)); 335 Main.ds.fireSelectionChanged(sel); 336 selectionChanged(sel); // update table 337 Main.parent.repaint(); // repaint all - drawing could have been changed 338 } 339 340 /** 341 * @param allData 342 * @param keys 343 * @param values 344 */ 345 private void addFocusAdapter(final int row, final TreeMap<String, TreeSet<String>> allData,final AutoCompleteComboBox keys, final AutoCompleteComboBox values) { 346 // get the combo box' editor component 347 JTextComponent editor = (JTextComponent)values.getEditor() 348 .getEditorComponent(); 349 // Refresh the values model when focus is gained 350 editor.addFocusListener(new FocusAdapter() { 351 @Override public void focusGained(FocusEvent e) { 352 String key = keys.getEditor().getItem().toString(); 353 updateListData(key, allData, values); 354 objKey=key; 355 } 356 }); 357 } 358 private String objKey; 359 /** 360 * @return 361 */ 362 private TreeMap<String, TreeSet<String>> createAutoCompletionInfo( 363 boolean edit) { 364 final TreeMap<String, TreeSet<String>> allData = new TreeMap<String, TreeSet<String>>(); 365 for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) { 366 for (String key : osm.keySet()) { 367 TreeSet<String> values = null; 368 if (allData.containsKey(key)) 369 values = allData.get(key); 370 else { 371 values = new TreeSet<String>(); 372 allData.put(key, values); 373 } 374 values.add(osm.get(key)); 375 } 376 } 377 if (!edit) { 378 for (int i = 0; i < propertyData.getRowCount(); ++i) 379 allData.remove(propertyData.getValueAt(i, 0)); 380 } 381 return allData; 382 } 383 384 /** 385 * Delete the keys from the given row. 386 * @param row The row, which key gets deleted from the dataset. 387 */ 388 private void delete(int row) { 389 String key = propertyData.getValueAt(row, 0).toString(); 390 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 391 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, null)); 392 Main.ds.fireSelectionChanged(sel); 393 selectionChanged(sel); // update table 394 } 395 396 /** 397 * The property data. 398 */ 399 private final DefaultTableModel propertyData = new DefaultTableModel() { 400 @Override public boolean isCellEditable(int row, int column) { 401 return false; 402 } 403 @Override public Class<?> getColumnClass(int columnIndex) { 404 return String.class; 405 } 406 }; 407 408 /** 409 * The membership data. 410 */ 411 private final DefaultTableModel membershipData = new DefaultTableModel() { 412 @Override public boolean isCellEditable(int row, int column) { 413 return false; 414 } 415 @Override public Class<?> getColumnClass(int columnIndex) { 416 return String.class; 417 } 418 }; 419 420 /** 421 * The properties list. 422 */ 423 private final JTable propertyTable = new JTable(propertyData); 424 private final JTable membershipTable = new JTable(membershipData); 425 426 public JComboBox taggingPresets = new JComboBox(); 427 428 429 /** 430 * Create a new PropertiesDialog 431 */ 432 public PropertiesDialog(MapFrame mapFrame) { 433 super(tr("Properties/Memberships"), "propertiesdialog", tr("Properties for selected objects."), 434 Shortcut.registerShortcut("subwindow:properties", tr("Toggle: {0}", tr("Properties/Memberships")), KeyEvent.VK_P, 435 Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150); 436 437 // --------------------------------------- 438 // This drop-down is really deprecated but we offer people a chance to 439 // activate it if they really want. Presets should be used from the 440 // menu. 441 if (TaggingPresetPreference.taggingPresets.size() > 0 && 442 Main.pref.getBoolean("taggingpreset.in-properties-dialog", false)) { 443 Vector<ActionListener> allPresets = new Vector<ActionListener>(); 444 for (final TaggingPreset p : TaggingPresetPreference.taggingPresets) 445 allPresets.add(new ForwardActionListener(this, p)); 446 447 TaggingPreset empty = new TaggingPreset(); 448 // empty.setName("this drop-down will be removed soon"); 449 allPresets.add(0, new ForwardActionListener(this, empty)); 450 taggingPresets.setModel(new DefaultComboBoxModel(allPresets)); 451 JPanel north = new JPanel(new GridBagLayout()); 452 north.add(getComponent(0),GBC.eol().fill(GBC.HORIZONTAL)); 453 north.add(taggingPresets,GBC.eol().fill(GBC.HORIZONTAL)); 454 add(north, BorderLayout.NORTH); 455 } 456 taggingPresets.addActionListener(new ActionListener(){ 457 public void actionPerformed(ActionEvent e) { 458 TaggingPreset preset = ((ForwardActionListener)taggingPresets.getSelectedItem()).preset; 459 preset.actionPerformed(e); 460 taggingPresets.setSelectedItem(null); 461 } 462 }); 463 taggingPresets.setRenderer(new TaggingCellRenderer()); 464 465 // setting up the properties table 466 467 propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")}); 468 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 469 470 propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){ 471 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 472 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column); 473 if (c instanceof JLabel) { 474 String str = null; 475 switch (column) { 476 case 0: 477 str = (String) value; 478 break; 479 case 1: 480 Map<String, Integer> v = (Map<String,Integer>) value; 481 if (v.size()!=1) { 482 str=tr("<different>"); 483 c.setFont(c.getFont().deriveFont(Font.ITALIC)); 484 } else { 485 str=v.entrySet().iterator().next().getKey(); 486 } 487 break; 488 } 489 ((JLabel)c).setText(str); 490 } 491 return c; 492 } 493 }); 494 495 // setting up the membership table 496 497 membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role")}); 498 membershipTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 499 500 membershipTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() { 501 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 502 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column); 503 if (c instanceof JLabel) { 504 nameVisitor.visit((Relation)value); 505 ((JLabel)c).setText(nameVisitor.name); 506 } 507 return c; 508 } 509 }); 510 511 membershipTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() { 512 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 513 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column); 514 if (c instanceof JLabel) { 515 Collection<RelationMember> col = (Collection<RelationMember>) value; 516 517 String text = null; 518 for (RelationMember r : col) { 519 if (text == null) { 520 text = r.role; 521 } 522 else if (!text.equals(r.role)) { 523 text = tr("<different>"); 524 break; 525 } 526 } 527 528 ((JLabel)c).setText(text); 529 } 530 return c; 531 } 532 }); 533 534 // combine both tables and wrap them in a scrollPane 535 JPanel bothTables = new JPanel(); 536 bothTables.setLayout(new GridBagLayout()); 537 bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL)); 538 bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH)); 539 bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL)); 540 bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH)); 541 542 DblClickWatch dblClickWatch = new DblClickWatch(); 543 propertyTable.addMouseListener(dblClickWatch); 544 membershipTable.addMouseListener(dblClickWatch); 545 JScrollPane scrollPane = new JScrollPane(bothTables); 546 scrollPane.addMouseListener(dblClickWatch); 547 add(scrollPane, BorderLayout.CENTER); 548 549 JPanel buttonPanel = new JPanel(new GridLayout(1,3)); 550 ActionListener buttonAction = new ActionListener(){ 551 public void actionPerformed(ActionEvent e) { 552 int row = membershipTable.getSelectedRow(); 553 if (e.getActionCommand().equals("Add")) 554 add(); 555 else if(row >= 0) 556 { 557 if (e.getActionCommand().equals("Edit")) 558 membershipEdit(row); 559 else if (e.getActionCommand().equals("Delete")) { 560 Relation cur = (Relation)membershipData.getValueAt(row, 0); 561 NameVisitor n = new NameVisitor(); 562 cur.visit(n); 563 if(JOptionPane.showConfirmDialog(Main.parent, tr("Really delete selection from relation {0}?", n.name), 564 tr("Change relation"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) 565 { 566 Relation rel = new Relation(cur); 567 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 568 for (RelationMember rm : cur.members) { 569 for (OsmPrimitive osm : sel) { 570 if (rm.member == osm) 571 { 572 RelationMember mem = new RelationMember(); 573 mem.role = rm.role; 574 mem.member = rm.member; 575 rel.members.remove(mem); 576 break; 577 } 578 } 579 } 580 Main.main.undoRedo.add(new ChangeCommand(cur, rel)); 581 Main.ds.fireSelectionChanged(sel); 582 selectionChanged(sel); // update whole table 583 } 584 585 } 586 } 587 else 588 { 589 int sel = propertyTable.getSelectedRow(); 590 if (e.getActionCommand().equals("Edit")) { 591 if(propertyTable.getRowCount() == 1) 592 sel = 0; 593 if (sel == -1) 594 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit.")); 595 else 596 propertyEdit(sel); 597 } else if (e.getActionCommand().equals("Delete")) { 598 if (sel == -1) 599 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete.")); 600 else 601 delete(sel); 602 } 603 } 604 } 605 }; 606 607 Shortcut s = Shortcut.registerShortcut("properties:add", tr("Add Properties"), KeyEvent.VK_B, 608 Shortcut.GROUP_MNEMONIC); 609 buttonPanel.add(new SideButton(marktr("Add"),"add","Properties", 610 610 tr("Add a new key/value pair to all objects"), s, buttonAction)); 611 611 612 613 614 612 s = Shortcut.registerShortcut("properties:edit", tr("Edit Properties"), KeyEvent.VK_I, 613 Shortcut.GROUP_MNEMONIC); 614 buttonPanel.add(new SideButton(marktr("Edit"),"edit","Properties", 615 615 tr("Edit the value of the selected key for all objects"), s, buttonAction)); 616 616 617 618 619 617 s = Shortcut.registerShortcut("properties:delete", tr("Delete Properties"), KeyEvent.VK_Q, 618 Shortcut.GROUP_MNEMONIC); 619 buttonPanel.add(new SideButton(marktr("Delete"),"delete","Properties", 620 620 tr("Delete the selected key in all objects"), s, buttonAction)); 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 621 add(buttonPanel, BorderLayout.SOUTH); 622 623 DataSet.selListeners.add(this); 624 } 625 626 @Override public void setVisible(boolean b) { 627 super.setVisible(b); 628 if (b) 629 selectionChanged(Main.ds.getSelected()); 630 } 631 632 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 633 if (!isVisible()) 634 return; 635 if (propertyTable == null) 636 return; // selection changed may be received in base class constructor before init 637 if (propertyTable.getCellEditor() != null) 638 propertyTable.getCellEditor().cancelCellEditing(); 639 640 // re-load property data 641 642 propertyData.setRowCount(0); 643 644 Map<String, Integer> keyCount = new HashMap<String, Integer>(); 645 valueCount.clear(); 646 for (OsmPrimitive osm : newSelection) { 647 for (Entry<String, String> e : osm.entrySet()) { 648 keyCount.put(e.getKey(), keyCount.containsKey(e.getKey()) ? keyCount.get(e.getKey())+1 : 1); 649 if (valueCount.containsKey(e.getKey())) { 650 Map<String, Integer> v = valueCount.get(e.getKey()); 651 v.put(e.getValue(), v.containsKey(e.getValue())? v.get(e.getValue())+1 : 1 ); 652 } else { 653 TreeMap<String,Integer> v = new TreeMap<String, Integer>(); 654 v.put(e.getValue(), 1); 655 valueCount.put(e.getKey(), v); 656 } 657 } 658 } 659 for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) { 660 int count=0; 661 for (Entry<String, Integer> e1: e.getValue().entrySet()) { 662 count+=e1.getValue(); 663 } 664 if (count < newSelection.size()) { 665 e.getValue().put("", newSelection.size()-count); 666 } 667 propertyData.addRow(new Object[]{e.getKey(), e.getValue()}); 668 } 669 670 // re-load membership data 671 // this is rather expensive since we have to walk through all members of all existing relationships. 672 // could use back references here for speed if necessary. 673 674 membershipData.setRowCount(0); 675 676 Map<Relation, Collection<RelationMember>> roles = new HashMap<Relation, Collection<RelationMember>>(); 677 for (Relation r : Main.ds.relations) { 678 if (!r.deleted && !r.incomplete) { 679 for (RelationMember m : r.members) { 680 if (newSelection.contains(m.member)) { 681 Collection<RelationMember> value = roles.get(r); 682 if (value == null) { 683 value = new HashSet<RelationMember>(); 684 roles.put(r, value); 685 } 686 value.add(m); 687 } 688 } 689 } 690 } 691 692 for (Entry<Relation, Collection<RelationMember>> e : roles.entrySet()) { 693 membershipData.addRow(new Object[]{e.getKey(), e.getValue()}); 694 } 695 696 membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0); 697 } 698 698 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java
r1146 r1169 65 65 public class RelationEditor extends JFrame { 66 66 67 68 69 70 71 72 73 67 /** 68 * The relation that this editor is working on, and the clone made for 69 * editing. 70 */ 71 private final Relation relation; 72 private final Relation clone; 73 private JLabel status; 74 74 75 75 /** … … 78 78 boolean ordered; 79 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 80 /** 81 * The property data. 82 */ 83 private final DefaultTableModel propertyData = new DefaultTableModel() { 84 @Override public boolean isCellEditable(int row, int column) { 85 return true; 86 } 87 @Override public Class<?> getColumnClass(int columnIndex) { 88 return String.class; 89 } 90 }; 91 92 /** 93 * The membership data. 94 */ 95 private final DefaultTableModel memberData = new DefaultTableModel() { 96 @Override public boolean isCellEditable(int row, int column) { 97 return column == 0; 98 } 99 @Override public Class<?> getColumnClass(int columnIndex) { 100 return columnIndex == 1 ? OsmPrimitive.class : String.class; 101 } 102 }; 103 104 /** 105 * The properties and membership lists. 106 */ 107 private final JTable propertyTable = new JTable(propertyData); 108 private final JTable memberTable = new JTable(memberData); 109 109 110 110 // =================== FIXME FIXME FIXME ===================== 111 111 // As soon as API 0.5 is dead, drop all the collation stuff from here ... 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 112 113 /** 114 * Collator for sorting the roles and entries of the member table. 115 */ 116 private static final Collator collator; 117 static { 118 collator = Collator.getInstance(); 119 collator.setStrength(Collator.PRIMARY); 120 } 121 122 /** 123 * Compare role strings. 124 */ 125 private static int compareRole(String s1, String s2) { 126 int last1 = s1.lastIndexOf('_'); 127 if (last1 > 0) { 128 int last2 = s2.lastIndexOf('_'); 129 if (last2 == last1) { 130 String prefix1 = s1.substring(0, last1); 131 String prefix2 = s2.substring(0, last2); 132 133 if (prefix1.equalsIgnoreCase(prefix2)) { 134 // Both roles have the same prefix, now determine the 135 // suffix. 136 String suffix1 = s1.substring(last1 + 1, s1.length()); 137 String suffix2 = s2.substring(last2 + 1, s2.length()); 138 139 if (suffix1.matches("\\d+") && suffix2.matches("\\d+")) { 140 // Suffix is an number -> compare it. 141 int i1 = Integer.parseInt(suffix1); 142 int i2 = Integer.parseInt(suffix2); 143 144 return i1 - i2; 145 } 146 } 147 } 148 } 149 if(s1.length() == 0 && s2.length() != 0) 150 return 1; 151 else if(s2.length() == 0 && s1.length() != 0) 152 return -1; 153 154 // Default handling if the role name is nothing like "stop_xx" 155 return collator.compare(s1, s2); 156 } 157 158 159 /** 160 * Compare two OsmPrimitives. 161 */ 162 private static int compareMemebers(OsmPrimitive o1, OsmPrimitive o2) { 163 return collator.compare(o1.getName(), o2.getName()); 164 } 165 166 private final Comparator<RelationMember> memberComparator = new Comparator<RelationMember>() { 167 public int compare(RelationMember r1, RelationMember r2) { 168 int roleResult = compareRole(r1.role, r2.role); 169 170 if (roleResult == 0) 171 roleResult = compareMemebers(r1.member, r2.member); 172 173 return roleResult; 174 } 175 }; 176 176 177 177 // =================== FIXME FIXME FIXME ===================== 178 178 // ... until here, and also get rid of the "Collections.sort..." below. 179 180 181 182 183 * 184 185 * 186 187 188 189 190 191 192 193 194 195 196 * 197 198 * 199 200 201 202 203 204 205 206 179 180 /** 181 * Creates a new relation editor for the given relation. The relation 182 * will be saved if the user selects "ok" in the editor. 183 * 184 * If no relation is given, will create an editor for a new relation. 185 * 186 * @param relation relation to edit, or null to create a new one. 187 */ 188 public RelationEditor(Relation relation) 189 { 190 this(relation, null); 191 } 192 193 /** 194 * Creates a new relation editor for the given relation. The relation 195 * will be saved if the user selects "ok" in the editor. 196 * 197 * If no relation is given, will create an editor for a new relation. 198 * 199 * @param relation relation to edit, or null to create a new one. 200 */ 201 public RelationEditor(Relation relation, Collection<RelationMember> selectedMembers ) 202 { 203 super(relation == null ? tr("Create new relation") : 204 relation.id == 0 ? tr ("Edit new relation") : 205 tr("Edit relation #{0}", relation.id)); 206 this.relation = relation; 207 207 208 208 ordered = Main.pref.get("osm-server.version", "0.5").equals("0.6"); 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 209 210 if (relation == null) { 211 // create a new relation 212 this.clone = new Relation(); 213 } else { 214 // edit an existing relation 215 this.clone = new Relation(relation); 216 if (!ordered) Collections.sort(this.clone.members, memberComparator); 217 } 218 219 getContentPane().setLayout(new BorderLayout()); 220 JTabbedPane tabPane = new JTabbedPane(); 221 getContentPane().add(tabPane, BorderLayout.CENTER); 222 223 // (ab)use JOptionPane to make this look familiar; 224 // hook up with JOptionPane's property change event 225 // to detect button click 226 final JOptionPane okcancel = new JOptionPane("", 227 JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null); 228 getContentPane().add(okcancel, BorderLayout.SOUTH); 229 230 okcancel.addPropertyChangeListener(new PropertyChangeListener() { 231 public void propertyChange(PropertyChangeEvent event) { 232 if (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY) && event.getNewValue() != null) { 233 if ((Integer)event.getNewValue() == JOptionPane.OK_OPTION) { 234 // clicked ok! 235 if (RelationEditor.this.relation == null) { 236 Main.main.undoRedo.add(new AddCommand(clone)); 237 DataSet.fireSelectionChanged(Main.ds.getSelected()); 238 } else if (!RelationEditor.this.relation.realEqual(clone, true)) { 239 Main.main.undoRedo.add(new ChangeCommand(RelationEditor.this.relation, clone)); 240 DataSet.fireSelectionChanged(Main.ds.getSelected()); 241 } 242 } 243 setVisible(false); 244 } 245 } 246 }); 247 248 JLabel help = new JLabel("<html><em>"+ 249 tr("This is the basic relation editor which allows you to change the relation's tags " + 250 "as well as the members. In addition to this we should have a smart editor that " + 251 "detects the type of relationship and limits your choices in a sensible way.")+"</em></html>"); 252 getContentPane().add(help, BorderLayout.NORTH); 253 try { setAlwaysOnTop(true); } catch (SecurityException sx) {} 254 255 // Basic Editor panel has two blocks; 256 // a tag table at the top and a membership list below. 257 258 // setting up the properties table 259 260 propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")}); 261 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 262 propertyData.addTableModelListener(new TableModelListener() { 263 public void tableChanged(TableModelEvent tme) { 264 if (tme.getType() == TableModelEvent.UPDATE) { 265 int row = tme.getFirstRow(); 266 267 if (!(tme.getColumn() == 0 && row == propertyData.getRowCount() -1)) { 268 clone.entrySet().clear(); 269 for (int i = 0; i < propertyData.getRowCount(); i++) { 270 String key = propertyData.getValueAt(i, 0).toString(); 271 String value = propertyData.getValueAt(i, 1).toString(); 272 if (key.length() > 0 && value.length() > 0) clone.put(key, value); 273 } 274 refreshTables(); 275 } 276 } 277 } 278 }); 279 propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); 280 281 // setting up the member table 282 283 memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By")}); 284 memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 285 memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer()); 286 memberData.addTableModelListener(new TableModelListener() { 287 public void tableChanged(TableModelEvent tme) { 288 if (tme.getType() == TableModelEvent.UPDATE && tme.getColumn() == 0) { 289 int row = tme.getFirstRow(); 290 clone.members.get(row).role = memberData.getValueAt(row, 0).toString(); 291 } 292 } 293 }); 294 memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); 295 296 // combine both tables and wrap them in a scrollPane 297 JPanel bothTables = new JPanel(); 298 bothTables.setLayout(new GridBagLayout()); 299 bothTables.add(new JLabel(tr("Tags (empty value deletes tag)")), GBC.eol().fill(GBC.HORIZONTAL)); 300 bothTables.add(new JScrollPane(propertyTable), GBC.eop().fill(GBC.BOTH)); 301 bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL)); 302 302 if (ordered) { 303 303 JPanel upDownPanel = new JPanel(); 304 304 upDownPanel.setLayout(new BoxLayout(upDownPanel, BoxLayout.Y_AXIS)); 305 305 306 306 307 307 308 308 upDownPanel.add(createButton(null, "moveup", tr("Move the currently selected member(s) up"), … … 319 319 })); 320 320 321 321 322 322 bothTables.add(new JScrollPane(memberTable), GBC.std().fill(GBC.BOTH)); 323 323 bothTables.add(upDownPanel, GBC.eol().fill(GBC.VERTICAL)); … … 325 325 bothTables.add(new JScrollPane(memberTable), GBC.eol().fill(GBC.BOTH)); 326 326 } 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 for (int i = 0; i < memberData.getRowCount(); i++) { 396 397 398 399 400 401 402 403 404 405 406 407 408 409 } 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 327 328 JPanel buttonPanel = new JPanel(new GridLayout(1,3)); 329 330 buttonPanel.add(createButton(marktr("Add Selected"),"addselected", 331 tr("Add all currently selected objects as members"), KeyEvent.VK_A, new ActionListener() { 332 public void actionPerformed(ActionEvent e) { 333 addSelected(); 334 } 335 })); 336 337 buttonPanel.add(createButton(marktr("Delete Selected"),"deleteselected", 338 tr("Delete all currently selected objects from relation"), KeyEvent.VK_R, new ActionListener() { 339 public void actionPerformed(ActionEvent e) { 340 deleteSelected(); 341 } 342 })); 343 344 buttonPanel.add(createButton(marktr("Delete"),"delete", 345 tr("Remove the member in the current table row from this relation"), KeyEvent.VK_D, new ActionListener() { 346 public void actionPerformed(ActionEvent e) { 347 int[] rows = memberTable.getSelectedRows(); 348 RelationMember mem = new RelationMember(); 349 for (int row : rows) { 350 mem.role = memberTable.getValueAt(row, 0).toString(); 351 mem.member = (OsmPrimitive) memberTable.getValueAt(row, 1); 352 clone.members.remove(mem); 353 } 354 refreshTables(); 355 } 356 })); 357 358 buttonPanel.add(createButton(marktr("Select"),"select", 359 tr("Highlight the member from the current table row as JOSM's selection"), KeyEvent.VK_S, new ActionListener() { 360 public void actionPerformed(ActionEvent e) { 361 ArrayList<OsmPrimitive> sel; 362 int cnt = memberTable.getSelectedRowCount(); 363 if(cnt > 0) 364 { 365 sel = new ArrayList<OsmPrimitive>(cnt); 366 for (int i : memberTable.getSelectedRows()) 367 sel.add((OsmPrimitive)memberTable.getValueAt(i, 1)); 368 } 369 else 370 { 371 cnt = memberTable.getRowCount(); 372 sel = new ArrayList<OsmPrimitive>(cnt); 373 for (int i = 0; i < cnt; ++i) 374 sel.add((OsmPrimitive)memberTable.getValueAt(i, 1)); 375 } 376 Main.ds.setSelected(sel); 377 } 378 })); 379 buttonPanel.add(createButton(marktr("Download Members"),"down", 380 tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_L, new ActionListener() { 381 public void actionPerformed(ActionEvent e) { 382 downloadRelationMembers(); 383 refreshTables(); 384 } 385 })); 386 387 bothTables.add(buttonPanel, GBC.eop().fill(GBC.HORIZONTAL)); 388 389 tabPane.add(bothTables, "Basic"); 390 391 refreshTables(); 392 393 if (selectedMembers != null) { 394 boolean scrolled = false; 395 for (int i = 0; i < memberData.getRowCount(); i++) { 396 for (RelationMember m : selectedMembers) { 397 if (m.member == memberData.getValueAt(i, 1) 398 && m.role.equals(memberData.getValueAt(i, 0))) { 399 memberTable.addRowSelectionInterval(i, i); 400 if (!scrolled) { 401 // Ensure that the first member is visible 402 memberTable.scrollRectToVisible(memberTable.getCellRect(i, 0, true)); 403 scrolled = true; 404 } 405 break; 406 } 407 } 408 409 } 410 } 411 412 setSize(new Dimension(600, 500)); 413 setLocationRelativeTo(Main.parent); 414 } 415 416 private void refreshTables() { 417 // re-load property data 418 419 propertyData.setRowCount(0); 420 for (Entry<String, String> e : clone.entrySet()) { 421 propertyData.addRow(new Object[]{e.getKey(), e.getValue()}); 422 } 423 propertyData.addRow(new Object[]{"", ""}); 424 425 // re-load membership data 426 427 memberData.setRowCount(0); 428 for (RelationMember em : clone.members) { 429 memberData.addRow(new Object[]{em.role, em.member}); 430 } 431 status.setText(tr("Members: {0}", clone.members.size())); 432 } 433 434 private JButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) { 435 JButton b = new JButton(tr(name), ImageProvider.get("dialogs", iconName)); 436 b.setActionCommand(name); 437 b.addActionListener(actionListener); 438 b.setToolTipText(tooltip); 439 b.setMnemonic(mnemonic); 440 b.putClientProperty("help", "Dialog/Properties/"+name); 441 return b; 442 } 443 444 private void addSelected() { 445 for (OsmPrimitive p : Main.ds.getSelected()) { 446 boolean skip = false; 447 447 // ordered relations may have the same member multiple times. 448 448 // TODO: visual indication of the fact that one is there more than once? … … 457 457 } 458 458 } 459 460 461 462 463 459 if (!skip) 460 { 461 RelationMember em = new RelationMember(); 462 em.member = p; 463 em.role = ""; 464 464 // when working with ordered relations, we make an effort to 465 465 // add the element before the first selected member. … … 470 470 clone.members.add(em); 471 471 } 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 472 } 473 } 474 refreshTables(); 475 } 476 477 private void deleteSelected() { 478 for (OsmPrimitive p : Main.ds.getSelected()) { 479 Relation c = new Relation(clone); 480 for (RelationMember rm : c.members) { 481 if (rm.member == p) 482 { 483 RelationMember mem = new RelationMember(); 484 mem.role = rm.role; 485 mem.member = rm.member; 486 clone.members.remove(mem); 487 } 488 } 489 } 490 refreshTables(); 491 } 492 493 private void moveMembers(int direction) { 494 int[] rows = memberTable.getSelectedRows(); 495 495 if (rows.length == 0) return; 496 496 497 497 // check if user attempted to move anything beyond the boundary of the list 498 498 if (rows[0] + direction < 0) return; 499 499 if (rows[rows.length-1] + direction >= clone.members.size()) return; 500 500 501 501 RelationMember m[] = new RelationMember[clone.members.size()]; 502 502 503 503 // first move all selected rows from the member list into a new array, 504 504 // displaced by the move amount 505 505 for (Integer i: rows) { 506 506 m[i+direction] = clone.members.get(i); 507 clone.members.set(i, null); 508 } 509 507 clone.members.set(i, null); 508 } 509 510 510 // now fill the empty spots in the destination array with the remaining 511 511 // elements. … … 517 517 } 518 518 } 519 519 520 520 // and write the array back into the member list. 521 521 clone.members.clear(); 522 clone.members.addAll(Arrays.asList(m)); 523 522 clone.members.addAll(Arrays.asList(m)); 523 refreshTables(); 524 524 ListSelectionModel lsm = memberTable.getSelectionModel(); 525 525 lsm.setValueIsAdjusting(true); … … 528 528 } 529 529 lsm.setValueIsAdjusting(false); 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 530 } 531 532 private void downloadRelationMembers() { 533 boolean download = false; 534 for (RelationMember member : clone.members) { 535 if (member.member.incomplete) { 536 download = true; 537 break; 538 } 539 } 540 if (download) { 541 OsmServerObjectReader reader = new OsmServerObjectReader(clone.id, OsmServerObjectReader.TYPE_REL, true); 542 try { 543 DataSet dataSet = reader.parseOsm(); 544 if (dataSet != null) { 545 final MergeVisitor visitor = new MergeVisitor(Main.main 546 .editLayer().data, dataSet); 547 for (final OsmPrimitive osm : dataSet.allPrimitives()) 548 osm.visit(visitor); 549 visitor.fixReferences(); 550 551 // copy the merged layer's data source info 552 for (DataSource src : dataSet.dataSources) 553 Main.main.editLayer().data.dataSources.add(src); 554 Main.main.editLayer().fireDataChange(); 555 556 if (visitor.conflicts.isEmpty()) 557 return; 558 final ConflictDialog dlg = Main.map.conflictDialog; 559 dlg.add(visitor.conflicts); 560 JOptionPane.showMessageDialog(Main.parent, 561 tr("There were conflicts during import.")); 562 if (!dlg.isVisible()) 563 dlg.action 564 .actionPerformed(new ActionEvent(this, 0, "")); 565 } 566 567 } catch (SAXException e) { 568 e.printStackTrace(); 569 JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getMessage(), 570 tr("Error"), JOptionPane.ERROR_MESSAGE); 571 } catch (IOException e) { 572 e.printStackTrace(); 573 JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getMessage(), 574 tr("Error"), JOptionPane.ERROR_MESSAGE); 575 } 576 } 577 } 578 578 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java
r1084 r1169 45 45 public class RelationListDialog extends ToggleDialog implements LayerChangeListener, DataChangeListener { 46 46 47 48 49 50 47 /** 48 * The selection's list data. 49 */ 50 private final DefaultListModel list = new DefaultListModel(); 51 51 52 53 54 55 52 /** 53 * The display list. 54 */ 55 private JList displaylist = new JList(list); 56 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 57 public RelationListDialog() { 58 super(tr("Relations"), "relationlist", tr("Open a list of all relations."), 59 Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")), KeyEvent.VK_R, Shortcut.GROUP_LAYER), 150); 60 displaylist.setCellRenderer(new OsmPrimitivRenderer()); 61 displaylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 62 displaylist.addMouseListener(new MouseAdapter(){ 63 @Override public void mouseClicked(MouseEvent e) { 64 if (e.getClickCount() < 2) 65 return; 66 Relation toEdit = (Relation) displaylist.getSelectedValue(); 67 if (toEdit != null) 68 new RelationEditor(toEdit).setVisible(true); 69 } 70 }); 71 71 72 72 add(new JScrollPane(displaylist), BorderLayout.CENTER); 73 73 74 74 JPanel buttonPanel = new JPanel(new GridLayout(1,4)); 75 75 76 77 78 79 80 81 76 buttonPanel.add(new SideButton(marktr("New"), "addrelation", "Selection", tr("Create a new relation"), new ActionListener() { 77 public void actionPerformed(ActionEvent e) { 78 // call relation editor with null argument to create new relation 79 new RelationEditor(null).setVisible(true); 80 } 81 }), GBC.std()); 82 82 83 84 85 86 87 88 83 buttonPanel.add(new SideButton(marktr("Select"), "select", "Selection", tr("Select this relation"), new ActionListener() { 84 public void actionPerformed(ActionEvent e) { 85 // replace selection with the relation from the list 86 Main.ds.setSelected((Relation)displaylist.getSelectedValue()); 87 } 88 }), GBC.std()); 89 89 90 91 92 93 94 95 96 90 buttonPanel.add(new SideButton(marktr("Edit"), "edit", "Selection", tr( "Open an editor for the selected relation"), new ActionListener() { 91 public void actionPerformed(ActionEvent e) { 92 Relation toEdit = (Relation) displaylist.getSelectedValue(); 93 if (toEdit != null) 94 new RelationEditor(toEdit).setVisible(true); 95 } 96 }), GBC.std()); 97 97 98 99 100 101 102 103 104 105 106 107 108 109 98 buttonPanel.add(new SideButton(marktr("Delete"), "delete", "Selection", tr("Delete the selected relation"), new ActionListener() { 99 public void actionPerformed(ActionEvent e) { 100 Relation toDelete = (Relation) displaylist.getSelectedValue(); 101 if (toDelete != null) { 102 Main.main.undoRedo.add( 103 new DeleteCommand(Collections.singleton(toDelete))); 104 } 105 } 106 }), GBC.eol()); 107 Layer.listeners.add(this); 108 add(buttonPanel, BorderLayout.SOUTH); 109 } 110 110 111 112 113 114 111 @Override public void setVisible(boolean b) { 112 super.setVisible(b); 113 if (b) updateList(); 114 } 115 115 116 117 118 119 120 121 122 123 124 116 public void updateList() { 117 list.setSize(Main.ds.relations.size()); 118 int i = 0; 119 for (OsmPrimitive e : DataSet.sort(Main.ds.relations)) { 120 if (!e.deleted && !e.incomplete) 121 list.setElementAt(e, i++); 122 } 123 list.setSize(i); 124 } 125 125 126 127 128 129 130 131 132 133 126 public void activeLayerChange(Layer a, Layer b) { 127 if ((a == null || a instanceof OsmDataLayer) && b instanceof OsmDataLayer) { 128 if (a != null) ((OsmDataLayer)a).listenerDataChanged.remove(this); 129 ((OsmDataLayer)b).listenerDataChanged.add(this); 130 updateList(); 131 repaint(); 132 } 133 } 134 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 135 public void layerRemoved(Layer a) { 136 if (a instanceof OsmDataLayer) { 137 ((OsmDataLayer)a).listenerDataChanged.remove(this); 138 } 139 } 140 public void layerAdded(Layer a) { 141 if (a instanceof OsmDataLayer) { 142 ((OsmDataLayer)a).listenerDataChanged.add(this); 143 } 144 } 145 public void dataChanged(OsmDataLayer l) { 146 updateList(); 147 repaint(); 148 } 149 149 150 151 152 153 154 155 156 157 150 /** 151 * Returns the currently selected relation, or null. 152 * 153 * @return the currently selected relation, or null 154 */ 155 public Relation getCurrentRelation() { 156 return (Relation) displaylist.getSelectedValue(); 157 } 158 158 159 160 161 162 163 164 165 166 159 /** 160 * Adds a selection listener to the relation list. 161 * 162 * @param listener the listener to add 163 */ 164 public void addListSelectionListener(ListSelectionListener listener) { 165 displaylist.addListSelectionListener(listener); 166 } 167 167 168 169 170 171 172 173 174 175 168 /** 169 * Removes a selection listener from the relation list. 170 * 171 * @param listener the listener to remove 172 */ 173 public void removeListSelectionListener(ListSelectionListener listener) { 174 displaylist.removeListSelectionListener(listener); 175 } 176 176 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java
r1084 r1169 36 36 public class ToggleDialog extends JPanel implements Helpful { 37 37 38 39 40 38 public final class ToggleDialogAction extends JosmAction { 39 public final String prefname; 40 public AbstractButton button; 41 41 42 43 44 45 42 private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut, String prefname) { 43 super(name, iconName, tooltip, shortcut, false); 44 this.prefname = prefname; 45 } 46 46 47 48 49 50 51 52 53 47 public void actionPerformed(ActionEvent e) { 48 if (e != null && !(e.getSource() instanceof AbstractButton)) 49 button.setSelected(!button.isSelected()); 50 setVisible(button.isSelected()); 51 Main.pref.put(prefname+".visible", button.isSelected()); 52 } 53 } 54 54 55 56 57 58 59 55 /** 56 * The action to toggle this dialog. 57 */ 58 public ToggleDialogAction action; 59 public final String prefName; 60 60 61 62 61 public JPanel parent; 62 private final JPanel titleBar = new JPanel(new GridBagLayout()); 63 63 64 65 66 67 68 69 64 @Deprecated 65 public ToggleDialog(final String name, String iconName, String tooltip, int shortcut, int preferredHeight) { 66 super(new BorderLayout()); 67 this.prefName = iconName; 68 ToggleDialogInit(name, iconName, tooltip, Shortcut.registerShortcut("auto:"+name, tooltip, shortcut, Shortcut.GROUP_LAYER), preferredHeight); 69 } 70 70 71 72 73 74 75 71 public ToggleDialog(final String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) { 72 super(new BorderLayout()); 73 this.prefName = iconName; 74 ToggleDialogInit(name, iconName, tooltip, shortcut, preferredHeight); 75 } 76 76 77 78 79 80 81 82 77 private void ToggleDialogInit(final String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) { 78 setPreferredSize(new Dimension(330,preferredHeight)); 79 action = new ToggleDialogAction(name, "dialogs/"+iconName, tooltip, shortcut, iconName); 80 String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1); 81 action.putValue("help", helpId.substring(0, helpId.length()-6)); 82 setLayout(new BorderLayout()); 83 83 84 85 84 titleBar.add(new JLabel(name), GBC.std()); 85 titleBar.add(Box.createHorizontalGlue(),GBC.std().fill(GBC.HORIZONTAL)); 86 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 87 JButton sticky = new JButton(ImageProvider.get("misc", "sticky")); 88 sticky.setBorder(BorderFactory.createEmptyBorder()); 89 final ActionListener stickyActionListener = new ActionListener(){ 90 public void actionPerformed(ActionEvent e) { 91 final JFrame f = new JFrame(name); 92 try {f.setAlwaysOnTop(true);} catch (SecurityException e1) {} 93 parent.remove(ToggleDialog.this); 94 f.getContentPane().add(ToggleDialog.this); 95 f.addWindowListener(new WindowAdapter(){ 96 @Override public void windowClosing(WindowEvent e) { 97 titleBar.setVisible(true); 98 f.getContentPane().removeAll(); 99 parent.add(ToggleDialog.this); 100 f.dispose(); 101 101 102 103 104 102 // doLayout() - workaround 103 setVisible(false); 104 setVisible(true); 105 105 106 Main.pref.put(action.prefname+".docked", true); 107 } 108 }); 109 f.addComponentListener(new ComponentAdapter(){ 110 @Override public void componentMoved(ComponentEvent e) { 111 Main.pref.put(action.prefname+".bounds", f.getX()+","+f.getY()+","+f.getWidth()+","+f.getHeight()); 106 Main.pref.put(action.prefname+".docked", true); 112 107 } 113 }); 114 String bounds = Main.pref.get(action.prefname+".bounds",null); 115 if (bounds != null) { 116 String[] b = bounds.split(","); 117 f.setBounds(Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3])); 118 } else 119 f.pack(); 120 Main.pref.put(action.prefname+".docked", false); 121 f.setVisible(true); 122 titleBar.setVisible(false); 108 }); 109 f.addComponentListener(new ComponentAdapter(){ 110 @Override public void componentMoved(ComponentEvent e) { 111 Main.pref.put(action.prefname+".bounds", f.getX()+","+f.getY()+","+f.getWidth()+","+f.getHeight()); 112 } 113 }); 114 String bounds = Main.pref.get(action.prefname+".bounds",null); 115 if (bounds != null) { 116 String[] b = bounds.split(","); 117 f.setBounds(Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3])); 118 } else 119 f.pack(); 120 Main.pref.put(action.prefname+".docked", false); 121 f.setVisible(true); 122 titleBar.setVisible(false); 123 123 124 125 126 127 128 129 124 // doLayout() - workaround 125 parent.setVisible(false); 126 parent.setVisible(true); 127 } 128 }; 129 sticky.addActionListener(stickyActionListener); 130 130 131 132 131 titleBar.add(sticky); 132 add(titleBar, BorderLayout.NORTH); 133 133 134 135 134 setVisible(false); 135 setBorder(BorderFactory.createEtchedBorder()); 136 136 137 138 139 140 137 if (!Main.pref.getBoolean(action.prefname+".docked", true)) { 138 EventQueue.invokeLater(new Runnable(){ 139 public void run() { 140 stickyActionListener.actionPerformed(null); 141 141 } 142 143 144 142 }); 143 } 144 } 145 145 146 147 148 149 150 146 public String helpTopic() { 147 String help = getClass().getName(); 148 help = help.substring(help.lastIndexOf('.')+1, help.length()-6); 149 return "Dialog/"+help; 150 } 151 151 } -
trunk/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java
r1084 r1169 34 34 public class UserListDialog extends ToggleDialog implements SelectionChangedListener, MouseListener{ 35 35 36 37 38 39 40 41 42 43 44 45 46 36 /** 37 * The display list. 38 */ 39 private final DefaultTableModel data = new DefaultTableModel() { 40 @Override public boolean isCellEditable(int row, int column) { 41 return false; 42 } 43 @Override public Class<?> getColumnClass(int columnIndex) { 44 return columnIndex == 0 ? String.class : Integer.class; 45 } 46 }; 47 47 48 48 private JTable userTable = new JTable(data); 49 49 50 50 private static User anonymousUser = User.get("(anonymous users)"); 51 51 52 53 54 52 public UserListDialog() { 53 super(tr("Authors"), "userlist", tr("Open a list of people working on the selected objects."), 54 Shortcut.registerShortcut("subwindow:authors", tr("Toggle: {0}", tr("Authors")), KeyEvent.VK_A, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150); 55 55 56 57 58 59 60 61 62 56 data.setColumnIdentifiers(new String[]{tr("Author"),tr("# Objects"),"%"}); 57 userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 58 add(new JScrollPane(userTable), BorderLayout.CENTER); 59 selectionChanged(Main.ds.getSelected()); 60 userTable.addMouseListener(this); 61 DataSet.selListeners.add(this); 62 } 63 63 64 65 66 67 68 64 @Override public void setVisible(boolean b) { 65 super.setVisible(b); 66 if (b) 67 selectionChanged(Main.ds.getSelected()); 68 } 69 69 70 71 72 73 74 75 76 70 /** 71 * Called when the selection in the dataset changed. 72 * @param newSelection The new selection array. 73 */ 74 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 75 if (!isVisible()) 76 return; 77 77 78 79 80 81 82 78 class UserCount { 79 User user; 80 int count; 81 UserCount(User user, int count) { this.user=user; this.count=count; } 82 } 83 83 84 85 84 if (data == null) 85 return; // selection changed may be received in base class constructor before init 86 86 87 87 data.setRowCount(0); 88 88 89 90 91 89 HashMap<User,UserCount> counters = new HashMap<User,UserCount>(); 90 int all = 0; 91 for (OsmPrimitive p : newSelection) { 92 92 User u = p.user; 93 93 if (u == null) u = anonymousUser; … … 97 97 uc.count++; 98 98 all++; 99 100 101 102 103 104 105 106 99 } 100 UserCount[] ucArr = new UserCount[counters.size()]; 101 counters.values().toArray(ucArr); 102 Arrays.sort(ucArr, new Comparator<UserCount>() { 103 public int compare(UserCount a, UserCount b) { 104 return (a.count<b.count) ? 1 : (a.count>b.count) ? -1 : 0; 105 } 106 }); 107 107 108 109 110 111 108 for (UserCount uc : ucArr) { 109 data.addRow(new Object[] { uc.user.name, uc.count, uc.count * 100 / all }); 110 } 111 } 112 112 113 113 public void mouseClicked(MouseEvent e) { … … 115 115 int index = userTable.getSelectedRow(); 116 116 String userName = (String) data.getValueAt(index, 0); 117 if (userName==null) 117 if (userName==null) 118 118 return; 119 119 Collection<OsmPrimitive> selected = Main.ds.getSelected(); -
trunk/src/org/openstreetmap/josm/gui/download/BookmarkSelection.java
r733 r1169 27 27 /** 28 28 * Bookmark selector. 29 * 29 * 30 30 * Provides selection, creation and deletion of bookmarks. 31 31 * Extracted from old DownloadAction. 32 * 32 * 33 33 * @author Frederik Ramm <frederik@remote.org> 34 34 * … … 36 36 public class BookmarkSelection implements DownloadSelection { 37 37 38 private Preferences.Bookmark tempBookmark = null; 39 private BookmarkList bookmarks; 40 41 public void addGui(final DownloadDialog gui) { 42 43 JPanel dlg = new JPanel(new GridBagLayout()); 44 gui.tabpane.addTab(tr("Bookmarks"), dlg); 38 private Preferences.Bookmark tempBookmark = null; 39 private BookmarkList bookmarks; 45 40 46 bookmarks = new BookmarkList(); 47 48 /* add a handler for "double click" mouse events */ 49 MouseListener mouseListener = new MouseAdapter() { 50 @Override public void mouseClicked(MouseEvent e) { 51 if (e.getClickCount() == 2) { 52 //int index = bookmarks.locationToIndex(e.getPoint()); 53 gui.closeDownloadDialog(true); 54 } 55 } 56 }; 57 bookmarks.addMouseListener(mouseListener); 58 59 bookmarks.getSelectionModel().addListSelectionListener(new ListSelectionListener() { 60 public void valueChanged(ListSelectionEvent e) { 61 Preferences.Bookmark b = (Preferences.Bookmark)bookmarks.getSelectedValue(); 62 if (b != null) { 63 gui.minlat = b.latlon[0]; 64 gui.minlon = b.latlon[1]; 65 gui.maxlat = b.latlon[2]; 66 gui.maxlon = b.latlon[3]; 67 gui.boundingBoxChanged(BookmarkSelection.this); 68 } 69 } 70 }); 71 //wc.addListMarker(bookmarks); 72 dlg.add(new JScrollPane(bookmarks), GBC.eol().fill()); 41 public void addGui(final DownloadDialog gui) { 73 42 74 JPanel buttons = new JPanel(new GridLayout(1,2)); 75 JButton add = new JButton(tr("Add")); 76 add.addActionListener(new ActionListener(){ 77 public void actionPerformed(ActionEvent e) { 78 79 if (tempBookmark == null) { 80 JOptionPane.showMessageDialog(Main.parent, tr("Please enter the desired coordinates first.")); 81 return; 82 } 83 tempBookmark.name = JOptionPane.showInputDialog(Main.parent,tr("Please enter a name for the location.")); 84 if (tempBookmark.name != null && !tempBookmark.name.equals("")) { 85 ((DefaultListModel)bookmarks.getModel()).addElement(tempBookmark); 86 bookmarks.save(); 87 } 88 } 89 }); 90 buttons.add(add); 91 JButton remove = new JButton(tr("Remove")); 92 remove.addActionListener(new ActionListener(){ 93 public void actionPerformed(ActionEvent e) { 94 Object sel = bookmarks.getSelectedValue(); 95 if (sel == null) { 96 JOptionPane.showMessageDialog(Main.parent,tr("Select a bookmark first.")); 97 return; 98 } 99 ((DefaultListModel)bookmarks.getModel()).removeElement(sel); 100 bookmarks.save(); 101 } 102 }); 103 buttons.add(remove); 104 dlg.add(buttons, GBC.eop().fill(GBC.HORIZONTAL)); 105 } 43 JPanel dlg = new JPanel(new GridBagLayout()); 44 gui.tabpane.addTab(tr("Bookmarks"), dlg); 106 45 107 public void boundingBoxChanged(DownloadDialog gui) { 108 tempBookmark = new Preferences.Bookmark(); 109 tempBookmark.latlon[0] = gui.minlat; 110 tempBookmark.latlon[1] = gui.minlon; 111 tempBookmark.latlon[2] = gui.maxlat; 112 tempBookmark.latlon[3] = gui.maxlon; 113 bookmarks.clearSelection(); 114 } 46 bookmarks = new BookmarkList(); 47 48 /* add a handler for "double click" mouse events */ 49 MouseListener mouseListener = new MouseAdapter() { 50 @Override public void mouseClicked(MouseEvent e) { 51 if (e.getClickCount() == 2) { 52 //int index = bookmarks.locationToIndex(e.getPoint()); 53 gui.closeDownloadDialog(true); 54 } 55 } 56 }; 57 bookmarks.addMouseListener(mouseListener); 58 59 bookmarks.getSelectionModel().addListSelectionListener(new ListSelectionListener() { 60 public void valueChanged(ListSelectionEvent e) { 61 Preferences.Bookmark b = (Preferences.Bookmark)bookmarks.getSelectedValue(); 62 if (b != null) { 63 gui.minlat = b.latlon[0]; 64 gui.minlon = b.latlon[1]; 65 gui.maxlat = b.latlon[2]; 66 gui.maxlon = b.latlon[3]; 67 gui.boundingBoxChanged(BookmarkSelection.this); 68 } 69 } 70 }); 71 //wc.addListMarker(bookmarks); 72 dlg.add(new JScrollPane(bookmarks), GBC.eol().fill()); 73 74 JPanel buttons = new JPanel(new GridLayout(1,2)); 75 JButton add = new JButton(tr("Add")); 76 add.addActionListener(new ActionListener(){ 77 public void actionPerformed(ActionEvent e) { 78 79 if (tempBookmark == null) { 80 JOptionPane.showMessageDialog(Main.parent, tr("Please enter the desired coordinates first.")); 81 return; 82 } 83 tempBookmark.name = JOptionPane.showInputDialog(Main.parent,tr("Please enter a name for the location.")); 84 if (tempBookmark.name != null && !tempBookmark.name.equals("")) { 85 ((DefaultListModel)bookmarks.getModel()).addElement(tempBookmark); 86 bookmarks.save(); 87 } 88 } 89 }); 90 buttons.add(add); 91 JButton remove = new JButton(tr("Remove")); 92 remove.addActionListener(new ActionListener(){ 93 public void actionPerformed(ActionEvent e) { 94 Object sel = bookmarks.getSelectedValue(); 95 if (sel == null) { 96 JOptionPane.showMessageDialog(Main.parent,tr("Select a bookmark first.")); 97 return; 98 } 99 ((DefaultListModel)bookmarks.getModel()).removeElement(sel); 100 bookmarks.save(); 101 } 102 }); 103 buttons.add(remove); 104 dlg.add(buttons, GBC.eop().fill(GBC.HORIZONTAL)); 105 } 106 107 public void boundingBoxChanged(DownloadDialog gui) { 108 tempBookmark = new Preferences.Bookmark(); 109 tempBookmark.latlon[0] = gui.minlat; 110 tempBookmark.latlon[1] = gui.minlon; 111 tempBookmark.latlon[2] = gui.maxlat; 112 tempBookmark.latlon[3] = gui.maxlon; 113 bookmarks.clearSelection(); 114 } 115 115 116 116 -
trunk/src/org/openstreetmap/josm/gui/download/BoundingBoxSelection.java
r1064 r1169 26 26 /** 27 27 * Bounding box selector. 28 * 28 * 29 29 * Provides max/min lat/lon input fields as well as the "URL from www.openstreetmap.org" text field. 30 * 30 * 31 31 * @author Frederik Ramm <frederik@remote.org> 32 32 * … … 34 34 public class BoundingBoxSelection implements DownloadSelection { 35 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 gui.minlat = minlat; gui.minlon = minlon; 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 } 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 for (JTextField f : latlon) 150 151 152 153 private void updateUrl(DownloadDialog gui) { 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 36 private JTextField[] latlon = new JTextField[] { 37 new JTextField(11), 38 new JTextField(11), 39 new JTextField(11), 40 new JTextField(11) }; 41 final JTextArea osmUrl = new JTextArea(); 42 final JTextArea showUrl = new JTextArea(); 43 String noteUrl = tr("You can paste an URL here to download the area."); 44 45 public void addGui(final DownloadDialog gui) { 46 47 JPanel dlg = new JPanel(new GridBagLayout()); 48 osmUrl.setText(noteUrl); 49 50 final FocusListener dialogUpdater = new FocusAdapter() { 51 @Override public void focusLost(FocusEvent e) { 52 SwingUtilities.invokeLater(new Runnable() { 53 public void run() { 54 try { 55 double minlat = Double.parseDouble(latlon[0].getText()); 56 double minlon = Double.parseDouble(latlon[1].getText()); 57 double maxlat = Double.parseDouble(latlon[2].getText()); 58 double maxlon = Double.parseDouble(latlon[3].getText()); 59 if (minlat != gui.minlat || minlon != gui.minlon || maxlat != gui.maxlat || maxlon != gui.maxlon) { 60 gui.minlat = minlat; gui.minlon = minlon; 61 gui.maxlat = maxlat; gui.maxlon = maxlon; 62 gui.boundingBoxChanged(BoundingBoxSelection.this); 63 } 64 } catch (NumberFormatException x) { 65 // ignore 66 } 67 updateUrl(gui); 68 } 69 }); 70 } 71 }; 72 73 for (JTextField f : latlon) { 74 f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height)); 75 f.addFocusListener(dialogUpdater); 76 } 77 class osmUrlRefresher implements DocumentListener { 78 public void changedUpdate(DocumentEvent e) { dowork(); } 79 public void insertUpdate(DocumentEvent e) { dowork(); } 80 public void removeUpdate(DocumentEvent e) { dowork(); } 81 private void dowork() { 82 Bounds b = osmurl2bounds(osmUrl.getText()); 83 if (b != null) { 84 gui.minlon = b.min.lon(); 85 gui.minlat = b.min.lat(); 86 gui.maxlon = b.max.lon(); 87 gui.maxlat = b.max.lat(); 88 gui.boundingBoxChanged(BoundingBoxSelection.this); 89 updateBboxFields(gui); 90 updateUrl(gui); 91 } 92 } 93 } 94 95 osmUrl.getDocument().addDocumentListener(new osmUrlRefresher()); 96 97 // select content on receiving focus. this seems to be the default in the 98 // windows look+feel but not for others. needs invokeLater to avoid strange 99 // side effects that will cancel out the newly made selection otherwise. 100 osmUrl.addFocusListener(new FocusAdapter() { 101 @Override public void focusGained(FocusEvent e) { 102 SwingUtilities.invokeLater(new Runnable() { 103 public void run() { 104 osmUrl.selectAll(); 105 } 106 }); 107 } 108 }); 109 osmUrl.setLineWrap(true); 110 osmUrl.setBorder(latlon[0].getBorder()); 111 112 dlg.add(new JLabel(tr("min lat")), GBC.std().insets(10,20,5,0)); 113 dlg.add(latlon[0], GBC.std().insets(0,20,0,0)); 114 dlg.add(new JLabel(tr("min lon")), GBC.std().insets(10,20,5,0)); 115 dlg.add(latlon[1], GBC.eol().insets(0,20,0,0)); 116 dlg.add(new JLabel(tr("max lat")), GBC.std().insets(10,0,5,0)); 117 dlg.add(latlon[2], GBC.std()); 118 dlg.add(new JLabel(tr("max lon")), GBC.std().insets(10,0,5,0)); 119 dlg.add(latlon[3], GBC.eol()); 120 121 dlg.add(new JLabel(tr("URL from www.openstreetmap.org")), GBC.eol().insets(10,20,5,0)); 122 dlg.add(osmUrl, GBC.eop().insets(10,0,5,0).fill()); 123 dlg.add(showUrl, GBC.eop().insets(10,0,5,5)); 124 showUrl.setEditable(false); 125 showUrl.setBackground(dlg.getBackground()); 126 showUrl.addFocusListener(new FocusAdapter(){ 127 @Override 128 public void focusGained(FocusEvent e) { 129 showUrl.selectAll(); 130 } 131 }); 132 133 gui.tabpane.addTab(tr("Bounding Box"), dlg); 134 } 135 136 /** 137 * Called when bounding box is changed by one of the other download dialog tabs. 138 */ 139 public void boundingBoxChanged(DownloadDialog gui) { 140 updateBboxFields(gui); 141 updateUrl(gui); 142 } 143 144 private void updateBboxFields(DownloadDialog gui) { 145 latlon[0].setText(Double.toString(gui.minlat)); 146 latlon[1].setText(Double.toString(gui.minlon)); 147 latlon[2].setText(Double.toString(gui.maxlat)); 148 latlon[3].setText(Double.toString(gui.maxlon)); 149 for (JTextField f : latlon) 150 f.setCaretPosition(0); 151 } 152 153 private void updateUrl(DownloadDialog gui) { 154 double lat = (gui.minlat + gui.maxlat)/2; 155 double lon = (gui.minlon + gui.maxlon)/2; 156 // convert to mercator (for calculation of zoom only) 157 double latMin = Math.log(Math.tan(Math.PI/4.0+gui.minlat/180.0*Math.PI/2.0))*180.0/Math.PI; 158 double latMax = Math.log(Math.tan(Math.PI/4.0+gui.maxlat/180.0*Math.PI/2.0))*180.0/Math.PI; 159 double size = Math.max(Math.abs(latMax-latMin), Math.abs(gui.maxlon-gui.minlon)); 160 int zoom = 0; 161 while (zoom <= 20) { 162 if (size >= 180) 163 break; 164 size *= 2; 165 zoom++; 166 } 167 showUrl.setText("http://www.openstreetmap.org/index.html?mlat="+lat+"&mlon="+lon+"&zoom="+zoom); 168 } 169 170 public static Bounds osmurl2bounds(String url) { 171 int i = url.indexOf('?'); 172 if (i == -1) 173 return null; 174 String[] args = url.substring(i+1).split("&"); 175 HashMap<String, String> map = new HashMap<String, String>(); 176 for (String arg : args) { 177 int eq = arg.indexOf('='); 178 if (eq != -1) { 179 map.put(arg.substring(0, eq), arg.substring(eq + 1)); 180 } 181 } 182 183 Bounds b = null; 184 try { 185 if (map.containsKey("bbox")) { 186 String bbox[] = map.get("bbox").split(","); 187 b = new Bounds( 188 new LatLon(Double.parseDouble(bbox[1]), Double.parseDouble(bbox[0])), 189 new LatLon(Double.parseDouble(bbox[3]), Double.parseDouble(bbox[2]))); 190 191 } else { 192 double size = 180.0 / Math.pow(2, Integer.parseInt(map.get("zoom"))); 193 b = new Bounds( 194 new LatLon(parseDouble(map, "lat") - size/2, parseDouble(map, "lon") - size), 195 new LatLon(parseDouble(map, "lat") + size/2, parseDouble(map, "lon") + size)); 196 } 197 } catch (NumberFormatException x) { 198 } catch (NullPointerException x) { 199 } 200 return b; 201 } 202 203 private static double parseDouble(HashMap<String, String> map, String key) { 204 if (map.containsKey(key)) 205 return Double.parseDouble(map.get(key)); 206 return Double.parseDouble(map.get("m"+key)); 207 207 } 208 208 } -
trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java
r1147 r1169 36 36 public class DownloadDialog extends JPanel { 37 37 38 39 38 // the JOptionPane that contains this dialog. required for the closeDialog() method. 39 private JOptionPane optionPane; 40 40 41 42 43 44 45 41 public interface DownloadTask { 42 /** 43 * Execute the download. 44 */ 45 void download(DownloadAction action, double minlat, double minlon, double maxlat, double maxlon); 46 46 void loadUrl(boolean newLayer, String url); 47 48 49 50 51 52 53 54 55 56 47 /** 48 * @return The checkbox presented to the user 49 */ 50 JCheckBox getCheckBox(); 51 /** 52 * @return The name of the preferences suffix to use for storing the 53 * selection state. 54 */ 55 String getPreferencesSuffix(); 56 } 57 57 58 59 60 61 62 63 58 /** 59 * The list of download tasks. First entry should be the osm data entry 60 * and the second the gps entry. After that, plugins can register additional 61 * download possibilities. 62 */ 63 public final List<DownloadTask> downloadTasks = new ArrayList<DownloadTask>(5); 64 64 65 66 67 68 65 public final List<DownloadSelection> downloadSelections = new ArrayList<DownloadSelection>(); 66 public final JTabbedPane tabpane = new JTabbedPane(); 67 public final JCheckBox newLayer; 68 public final JLabel sizeCheck = new JLabel(); 69 69 70 71 72 73 70 public double minlon; 71 public double minlat; 72 public double maxlon; 73 public double maxlat; 74 74 75 75 76 77 76 public DownloadDialog() { 77 setLayout(new GridBagLayout()); 78 78 79 80 79 downloadTasks.add(new DownloadOsmTask()); 80 downloadTasks.add(new DownloadGpsTask()); 81 81 82 83 84 85 86 87 88 89 90 82 // adding the download tasks 83 add(new JLabel(tr("Data Sources and Types")), GBC.eol().insets(0,5,0,0)); 84 for (DownloadTask task : downloadTasks) { 85 add(task.getCheckBox(), GBC.eol().insets(20,0,0,0)); 86 // don't override defaults, if we (initially) don't have any preferences 87 if(Main.pref.hasKey("download."+task.getPreferencesSuffix())) { 88 task.getCheckBox().setSelected(Main.pref.getBoolean("download."+task.getPreferencesSuffix())); 89 } 90 } 91 91 92 93 94 95 96 92 // predefined download selections 93 downloadSelections.add(new BoundingBoxSelection()); 94 downloadSelections.add(new TileSelection()); 95 downloadSelections.add(new BookmarkSelection()); 96 downloadSelections.add(new WorldChooser()); 97 97 98 99 100 101 98 // add selections from plugins 99 for (PluginProxy p : Main.plugins) { 100 p.addDownloadSelection(downloadSelections); 101 } 102 102 103 104 105 106 107 108 103 // now everybody may add their tab to the tabbed pane 104 // (not done right away to allow plugins to remove one of 105 // the default selectors!) 106 for (DownloadSelection s : downloadSelections) { 107 s.addGui(this); 108 } 109 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 110 if (Main.map != null) { 111 MapView mv = Main.map.mapView; 112 minlon = mv.getLatLon(0, mv.getHeight()).lon(); 113 minlat = mv.getLatLon(0, mv.getHeight()).lat(); 114 maxlon = mv.getLatLon(mv.getWidth(), 0).lon(); 115 maxlat = mv.getLatLon(mv.getWidth(), 0).lat(); 116 boundingBoxChanged(null); 117 } 118 else if (Main.pref.hasKey("osm-download.bounds")) { 119 // read the bounding box from the preferences 120 try { 121 String bounds[] = Main.pref.get("osm-download.bounds").split(";"); 122 minlat = Double.parseDouble(bounds[0]); 123 minlon = Double.parseDouble(bounds[1]); 124 maxlat = Double.parseDouble(bounds[2]); 125 maxlon = Double.parseDouble(bounds[3]); 126 boundingBoxChanged(null); 127 } 128 catch (Exception e) { 129 e.printStackTrace(); 130 } 131 } 132 132 133 134 133 newLayer = new JCheckBox(tr("Download as new layer"), Main.pref.getBoolean("download.newlayer", false)); 134 add(newLayer, GBC.eol().insets(0,5,0,0)); 135 135 136 137 136 add(new JLabel(tr("Download Area")), GBC.eol().insets(0,5,0,0)); 137 add(tabpane, GBC.eol().fill()); 138 138 139 140 141 142 143 139 try { 140 tabpane.setSelectedIndex(Integer.parseInt(Main.pref.get("download.tab", "0"))); 141 } catch (Exception ex) { 142 Main.pref.put("download.tab", "0"); 143 } 144 144 145 146 147 148 145 Font labelFont = sizeCheck.getFont(); 146 sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize())); 147 add(sizeCheck, GBC.eop().insets(0,5,5,10)); 148 } 149 149 150 151 152 153 154 155 156 157 158 150 private void updateSizeCheck() { 151 if ((maxlon-minlon)*(maxlat-minlat) > Main.pref.getDouble("osm-server.max-request-area", 0.25)) { 152 sizeCheck.setText(tr("Download area too large; will probably be rejected by server")); 153 sizeCheck.setForeground(Color.red); 154 } else { 155 sizeCheck.setText(tr("Download area ok, size probably acceptable to server")); 156 sizeCheck.setForeground(Color.darkGray); 157 } 158 } 159 159 160 161 162 163 164 165 166 167 168 169 170 171 172 160 /** 161 * Distributes a "bounding box changed" from one DownloadSelection 162 * object to the others, so they may update or clear their input 163 * fields. 164 * 165 * @param eventSource - the DownloadSelection object that fired this notification. 166 */ 167 public void boundingBoxChanged(DownloadSelection eventSource) { 168 for (DownloadSelection s : downloadSelections) { 169 if (s != eventSource) s.boundingBoxChanged(this); 170 } 171 updateSizeCheck(); 172 } 173 173 174 175 176 177 178 179 174 /* 175 * Returns currently selected tab. 176 */ 177 public int getSelectedTab() { 178 return tabpane.getSelectedIndex(); 179 } 180 180 181 182 183 184 185 186 187 188 189 181 /** 182 * Closes the download dialog. This is intended to be called by one of 183 * the various download area selection "plugins". 184 * 185 * @param download true to download selected data, false to cancel download 186 */ 187 public void closeDownloadDialog(boolean download) { 188 optionPane.setValue(download ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION); 189 } 190 190 191 192 193 194 195 196 197 191 /** 192 * Has to be called after this dialog has been added to a JOptionPane. 193 * @param optionPane 194 */ 195 public void setOptionPane(JOptionPane optionPane) { 196 this.optionPane = optionPane; 197 } 198 198 } -
trunk/src/org/openstreetmap/josm/gui/download/DownloadSelection.java
r627 r1169 4 4 5 5 public interface DownloadSelection { 6 7 * Add the GUI elements to the dialog. 8 9 6 /** 7 * Add the GUI elements to the dialog. 8 */ 9 void addGui(DownloadDialog gui); 10 10 11 /** 12 13 14 15 16 11 /** 12 * Update or clear display when a selection is made through another 13 * DownloadSelection object 14 */ 15 void boundingBoxChanged(DownloadDialog gui); 16 17 17 } -
trunk/src/org/openstreetmap/josm/gui/download/TileSelection.java
r979 r1169 19 19 /** 20 20 * Tile selector. 21 * 21 * 22 22 * Provides a tile coordinate input field. 23 * 23 * 24 24 * @author Frederik Ramm <frederik@remote.org> 25 25 * … … 27 27 public class TileSelection implements DownloadSelection { 28 28 29 private JTextField tileX0 = new JTextField(7); 30 private JTextField tileY0 = new JTextField(7); 31 private JTextField tileX1 = new JTextField(7); 32 private JTextField tileY1 = new JTextField(7); 33 private JSpinner tileZ = new JSpinner(new SpinnerNumberModel(12, 10, 18, 1)); 34 35 public void addGui(final DownloadDialog gui) { 29 private JTextField tileX0 = new JTextField(7); 30 private JTextField tileY0 = new JTextField(7); 31 private JTextField tileX1 = new JTextField(7); 32 private JTextField tileY1 = new JTextField(7); 33 private JSpinner tileZ = new JSpinner(new SpinnerNumberModel(12, 10, 18, 1)); 36 34 37 JPanel smpanel = new JPanel(new GridBagLayout()); 38 smpanel.add(new JLabel(tr("zoom level")), GBC.std().insets(0,0,10,0)); 39 smpanel.add(new JLabel(tr("x from")), GBC.std().insets(10,0,5,0)); 40 smpanel.add(tileX0, GBC.std()); 41 smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0)); 42 smpanel.add(tileX1, GBC.eol()); 43 smpanel.add(tileZ, GBC.std().insets(0,0,10,0)); 44 smpanel.add(new JLabel(tr("y from")), GBC.std().insets(10,0,5,0)); 45 smpanel.add(tileY0, GBC.std()); 46 smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0)); 47 smpanel.add(tileY1, GBC.eol()); 35 public void addGui(final DownloadDialog gui) { 48 36 49 final FocusListener dialogUpdater = new FocusAdapter() { 50 @Override public void focusLost(FocusEvent e) { 51 try { 52 int zoomlvl = (Integer) tileZ.getValue(); 53 int fromx = Integer.parseInt(tileX0.getText()); 54 int tox = fromx; 55 if (tileX1.getText().length()>0) { 56 tox = Integer.parseInt(tileX1.getText()); 57 } 58 if (tox<fromx) { int i = fromx; fromx=tox; tox=i; } 37 JPanel smpanel = new JPanel(new GridBagLayout()); 38 smpanel.add(new JLabel(tr("zoom level")), GBC.std().insets(0,0,10,0)); 39 smpanel.add(new JLabel(tr("x from")), GBC.std().insets(10,0,5,0)); 40 smpanel.add(tileX0, GBC.std()); 41 smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0)); 42 smpanel.add(tileX1, GBC.eol()); 43 smpanel.add(tileZ, GBC.std().insets(0,0,10,0)); 44 smpanel.add(new JLabel(tr("y from")), GBC.std().insets(10,0,5,0)); 45 smpanel.add(tileY0, GBC.std()); 46 smpanel.add(new JLabel(tr("to")), GBC.std().insets(10,0,5,0)); 47 smpanel.add(tileY1, GBC.eol()); 59 48 60 int fromy = Integer.parseInt(tileY0.getText()); 61 int toy = fromy; 62 if (tileY1.getText().length()>0) { 63 toy = Integer.parseInt(tileY1.getText()); 64 } 65 if (toy<fromy) { int i = fromy; fromy=toy; toy=i; } 49 final FocusListener dialogUpdater = new FocusAdapter() { 50 @Override public void focusLost(FocusEvent e) { 51 try { 52 int zoomlvl = (Integer) tileZ.getValue(); 53 int fromx = Integer.parseInt(tileX0.getText()); 54 int tox = fromx; 55 if (tileX1.getText().length()>0) { 56 tox = Integer.parseInt(tileX1.getText()); 57 } 58 if (tox<fromx) { int i = fromx; fromx=tox; tox=i; } 66 59 67 gui.minlat = tileYToLat(zoomlvl, toy+1); 68 gui.minlon = tileXToLon(zoomlvl, fromx); 69 gui.maxlat = tileYToLat(zoomlvl, fromy); 70 gui.maxlon = tileXToLon(zoomlvl, tox+1); 71 gui.boundingBoxChanged(TileSelection.this); 72 //repaint(); 73 } catch (NumberFormatException x) { 74 // ignore 75 } 76 } 77 }; 78 79 for (JTextField f : new JTextField[] { tileX0, tileX1, tileY0, tileY1 }) { 80 f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height)); 81 f.addFocusListener(dialogUpdater); 82 } 60 int fromy = Integer.parseInt(tileY0.getText()); 61 int toy = fromy; 62 if (tileY1.getText().length()>0) { 63 toy = Integer.parseInt(tileY1.getText()); 64 } 65 if (toy<fromy) { int i = fromy; fromy=toy; toy=i; } 83 66 84 gui.tabpane.addTab(tr("Tile Numbers"), smpanel); 85 } 86 87 /** 88 * Called when bounding box is changed by one of the other download dialog tabs. 89 */ 90 public void boundingBoxChanged(DownloadDialog gui) { 91 updateBboxFields(gui); 92 } 93 94 private void updateBboxFields(DownloadDialog gui) { 95 int z = ((Integer) tileZ.getValue()).intValue(); 96 tileX0.setText(Integer.toString(lonToTileX(z, gui.minlon))); 97 tileX1.setText(Integer.toString(lonToTileX(z, gui.maxlon-.00001))); 98 tileY0.setText(Integer.toString(latToTileY(z, gui.maxlat-.00001))); 99 tileY1.setText(Integer.toString(latToTileY(z, gui.minlat))); 100 } 101 102 public static int latToTileY(int zoom, double lat) { 103 if ((zoom < 3) || (zoom > 18)) return -1; 104 double l = lat / 180 * Math.PI; 105 double pf = Math.log(Math.tan(l) + (1/Math.cos(l))); 106 return (int) ((1<<(zoom-1)) * (Math.PI - pf) / Math.PI); 107 } 67 gui.minlat = tileYToLat(zoomlvl, toy+1); 68 gui.minlon = tileXToLon(zoomlvl, fromx); 69 gui.maxlat = tileYToLat(zoomlvl, fromy); 70 gui.maxlon = tileXToLon(zoomlvl, tox+1); 71 gui.boundingBoxChanged(TileSelection.this); 72 //repaint(); 73 } catch (NumberFormatException x) { 74 // ignore 75 } 76 } 77 }; 108 78 109 public static int lonToTileX(int zoom, double lon) {110 if ((zoom < 3) || (zoom > 18)) return -1; 111 return (int) ((1<<(zoom-3)) * (lon + 180.0) / 45.0);112 79 for (JTextField f : new JTextField[] { tileX0, tileX1, tileY0, tileY1 }) { 80 f.setMinimumSize(new Dimension(100,new JTextField().getMinimumSize().height)); 81 f.addFocusListener(dialogUpdater); 82 } 113 83 114 public static double tileYToLat(int zoom, int y) { 115 if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE; 116 return Math.atan(Math.sinh(Math.PI - (Math.PI*y / (1<<(zoom-1))))) * 180 / Math.PI; 117 } 84 gui.tabpane.addTab(tr("Tile Numbers"), smpanel); 85 } 118 86 119 public static double tileXToLon(int zoom, int x) { 120 if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE; 121 return x * 45.0 / (1<<(zoom-3)) - 180.0; 87 /** 88 * Called when bounding box is changed by one of the other download dialog tabs. 89 */ 90 public void boundingBoxChanged(DownloadDialog gui) { 91 updateBboxFields(gui); 92 } 122 93 123 } 94 private void updateBboxFields(DownloadDialog gui) { 95 int z = ((Integer) tileZ.getValue()).intValue(); 96 tileX0.setText(Integer.toString(lonToTileX(z, gui.minlon))); 97 tileX1.setText(Integer.toString(lonToTileX(z, gui.maxlon-.00001))); 98 tileY0.setText(Integer.toString(latToTileY(z, gui.maxlat-.00001))); 99 tileY1.setText(Integer.toString(latToTileY(z, gui.minlat))); 100 } 101 102 public static int latToTileY(int zoom, double lat) { 103 if ((zoom < 3) || (zoom > 18)) return -1; 104 double l = lat / 180 * Math.PI; 105 double pf = Math.log(Math.tan(l) + (1/Math.cos(l))); 106 return (int) ((1<<(zoom-1)) * (Math.PI - pf) / Math.PI); 107 } 108 109 public static int lonToTileX(int zoom, double lon) { 110 if ((zoom < 3) || (zoom > 18)) return -1; 111 return (int) ((1<<(zoom-3)) * (lon + 180.0) / 45.0); 112 } 113 114 public static double tileYToLat(int zoom, int y) { 115 if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE; 116 return Math.atan(Math.sinh(Math.PI - (Math.PI*y / (1<<(zoom-1))))) * 180 / Math.PI; 117 } 118 119 public static double tileXToLon(int zoom, int x) { 120 if ((zoom < 3) || (zoom > 18)) return Double.MIN_VALUE; 121 return x * 45.0 / (1<<(zoom-3)) - 180.0; 122 123 } 124 124 } -
trunk/src/org/openstreetmap/josm/gui/download/WorldChooser.java
r733 r1169 38 38 public class WorldChooser extends NavigatableComponent implements DownloadSelection { 39 39 40 41 42 43 40 /** 41 * The world as picture. 42 */ 43 private ImageIcon world; 44 44 45 46 47 48 45 /** 46 * Maximum scale level 47 */ 48 private double scaleMax; 49 49 50 51 52 53 50 /** 51 * Mark this rectangle (lat/lon values) when painting. 52 */ 53 private EastNorth markerMin, markerMax; 54 54 55 55 private Projection projection; 56 56 57 58 59 60 61 62 63 64 57 /** 58 * Create the chooser component. 59 */ 60 public WorldChooser() { 61 URL path = Main.class.getResource("/images/world.jpg"); 62 world = new ImageIcon(path); 63 center = new EastNorth(world.getIconWidth()/2, world.getIconHeight()/2); 64 setPreferredSize(new Dimension(400, 200)); 65 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 66 projection = new Projection() { 67 public EastNorth latlon2eastNorth(LatLon p) { 68 return new EastNorth( 69 (p.lon()+180) / 360 * world.getIconWidth(), 70 (p.lat()+90) / 180 * world.getIconHeight()); 71 } 72 public LatLon eastNorth2latlon(EastNorth p) { 73 return new LatLon( 74 p.north()*180/world.getIconHeight() - 90, 75 p.east()*360/world.getIconWidth() - 180); 76 } 77 @Override public String toString() { 78 return "WorldChooser"; 79 } 80 80 public String getCacheDirectoryName() { 81 81 throw new UnsupportedOperationException(); 82 82 } 83 84 83 public double scaleFactor() { 84 return 1.0 / world.getIconWidth(); 85 85 } 86 86 87 87 }; 88 88 89 90 91 89 MapScaler scaler = new MapScaler(this, projection); 90 add(scaler); 91 scaler.setLocation(10,10); 92 92 93 setMinimumSize(new Dimension(350, 350/2)); 94 } 95 96 public void addGui(final DownloadDialog gui) { 97 JPanel temp = new JPanel(); 98 temp.setLayout(new BorderLayout()); 99 temp.add(this, BorderLayout.CENTER); 100 temp.add(new JLabel(tr("You can use the mouse or Ctrl+Arrow keys/./ to zoom and pan.")), BorderLayout.SOUTH); 101 gui.tabpane.add(temp, tr("Map")); 102 new MapMover(this, temp); 103 SelectionEnded selListener = new SelectionEnded(){ 104 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) { 105 markerMin = getEastNorth(r.x, r.y+r.height); 106 markerMax = getEastNorth(r.x+r.width, r.y); 107 LatLon min = getProjection().eastNorth2latlon(markerMin); 108 LatLon max = getProjection().eastNorth2latlon(markerMax); 109 gui.minlat = min.lat(); 110 gui.minlon = min.lon(); 111 gui.maxlat = max.lat(); 112 gui.maxlon = max.lon(); 113 gui.boundingBoxChanged(WorldChooser.this); 114 repaint(); 115 } 116 public void addPropertyChangeListener(PropertyChangeListener listener) {} 117 public void removePropertyChangeListener(PropertyChangeListener listener) {} 118 }; 119 SelectionManager sm = new SelectionManager(selListener, false, this); 120 sm.register(this); 93 setMinimumSize(new Dimension(350, 350/2)); 121 94 } 122 95 123 public void boundingBoxChanged(DownloadDialog gui) { 124 markerMin = getProjection().latlon2eastNorth(new LatLon(gui.minlat, gui.minlon)); 125 markerMax = getProjection().latlon2eastNorth(new LatLon(gui.maxlat, gui.maxlon)); 126 repaint(); 127 } 96 public void addGui(final DownloadDialog gui) { 97 JPanel temp = new JPanel(); 98 temp.setLayout(new BorderLayout()); 99 temp.add(this, BorderLayout.CENTER); 100 temp.add(new JLabel(tr("You can use the mouse or Ctrl+Arrow keys/./ to zoom and pan.")), BorderLayout.SOUTH); 101 gui.tabpane.add(temp, tr("Map")); 102 new MapMover(this, temp); 103 SelectionEnded selListener = new SelectionEnded(){ 104 public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) { 105 markerMin = getEastNorth(r.x, r.y+r.height); 106 markerMax = getEastNorth(r.x+r.width, r.y); 107 LatLon min = getProjection().eastNorth2latlon(markerMin); 108 LatLon max = getProjection().eastNorth2latlon(markerMax); 109 gui.minlat = min.lat(); 110 gui.minlon = min.lon(); 111 gui.maxlat = max.lat(); 112 gui.maxlon = max.lon(); 113 gui.boundingBoxChanged(WorldChooser.this); 114 repaint(); 115 } 116 public void addPropertyChangeListener(PropertyChangeListener listener) {} 117 public void removePropertyChangeListener(PropertyChangeListener listener) {} 118 }; 119 SelectionManager sm = new SelectionManager(selListener, false, this); 120 sm.register(this); 121 } 128 122 129 /** 130 * Set the scale as well as the preferred size. 131 */ 132 @Override public void setPreferredSize(Dimension preferredSize) { 133 super.setPreferredSize(preferredSize); 134 scale = world.getIconWidth()/preferredSize.getWidth(); 135 scaleMax = scale; 136 } 123 public void boundingBoxChanged(DownloadDialog gui) { 124 markerMin = getProjection().latlon2eastNorth(new LatLon(gui.minlat, gui.minlon)); 125 markerMax = getProjection().latlon2eastNorth(new LatLon(gui.maxlat, gui.maxlon)); 126 repaint(); 127 } 137 128 138 /** 139 * Draw the current selected region. 140 */ 141 @Override public void paint(Graphics g) { 142 EastNorth tl = getEastNorth(0,0); 143 EastNorth br = getEastNorth(getWidth(),getHeight()); 144 g.drawImage(world.getImage(),0,0,getWidth(),getHeight(),(int)tl.east(),(int)tl.north(),(int)br.east(),(int)br.north(), null); 129 /** 130 * Set the scale as well as the preferred size. 131 */ 132 @Override public void setPreferredSize(Dimension preferredSize) { 133 super.setPreferredSize(preferredSize); 134 scale = world.getIconWidth()/preferredSize.getWidth(); 135 scaleMax = scale; 136 } 145 137 146 // draw marker rect 147 if (markerMin != null && markerMax != null) { 148 Point p1 = getPoint(markerMin); 149 Point p2 = getPoint(markerMax); 150 double x = Math.min(p1.x, p2.x); 151 double y = Math.min(p1.y, p2.y); 152 double w = Math.max(p1.x, p2.x) - x; 153 double h = Math.max(p1.y, p2.y) - y; 154 if (w < 1) 155 w = 1; 156 if (h < 1) 157 h = 1; 158 g.setColor(Color.YELLOW); 159 g.drawRect((int)x, (int)y, (int)w, (int)h); 160 } 161 super.paint(g); 162 } 138 /** 139 * Draw the current selected region. 140 */ 141 @Override public void paint(Graphics g) { 142 EastNorth tl = getEastNorth(0,0); 143 EastNorth br = getEastNorth(getWidth(),getHeight()); 144 g.drawImage(world.getImage(),0,0,getWidth(),getHeight(),(int)tl.east(),(int)tl.north(),(int)br.east(),(int)br.north(), null); 163 145 164 @Override public void zoomTo(EastNorth newCenter, double scale) { 165 if (getWidth() != 0 && scale > scaleMax) { 166 scale = scaleMax; 167 newCenter = center; 168 } 169 super.zoomTo(newCenter, scale); 170 } 146 // draw marker rect 147 if (markerMin != null && markerMax != null) { 148 Point p1 = getPoint(markerMin); 149 Point p2 = getPoint(markerMax); 150 double x = Math.min(p1.x, p2.x); 151 double y = Math.min(p1.y, p2.y); 152 double w = Math.max(p1.x, p2.x) - x; 153 double h = Math.max(p1.y, p2.y) - y; 154 if (w < 1) 155 w = 1; 156 if (h < 1) 157 h = 1; 158 g.setColor(Color.YELLOW); 159 g.drawRect((int)x, (int)y, (int)w, (int)h); 160 } 161 super.paint(g); 162 } 171 163 172 /** 173 * Always use our image projection mode. 174 */ 175 @Override protected Projection getProjection() { 176 return projection; 177 } 164 @Override public void zoomTo(EastNorth newCenter, double scale) { 165 if (getWidth() != 0 && scale > scaleMax) { 166 scale = scaleMax; 167 newCenter = center; 168 } 169 super.zoomTo(newCenter, scale); 170 } 171 172 /** 173 * Always use our image projection mode. 174 */ 175 @Override protected Projection getProjection() { 176 return projection; 177 } 178 178 } -
trunk/src/org/openstreetmap/josm/gui/layer/DataChangeListener.java
r627 r1169 4 4 public interface DataChangeListener { 5 5 6 7 6 public void dataChanged(OsmDataLayer l); 7 8 8 } -
trunk/src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java
r1152 r1169 78 78 public class GeoImageLayer extends Layer { 79 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 public void actionPerformed(ActionEvent ev) { 289 290 291 currentImage++; 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 // we ignore the first resize event, as the picture is scaled already on load: 332 333 334 335 336 } 337 338 339 340 341 342 dlg.setResizable(true); 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 private static BufferedImage createResizedCopy(Image originalImage, 538 539 540 541 542 543 g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null); 544 545 546 80 private static final class ImageEntry implements Comparable<ImageEntry> { 81 File image; 82 Date time; 83 LatLon coor; 84 EastNorth pos; 85 Icon icon; 86 public int compareTo(ImageEntry image) { 87 return time.compareTo(image.time); 88 } 89 } 90 91 private static final class Loader extends PleaseWaitRunnable { 92 boolean cancelled = false; 93 private GeoImageLayer layer; 94 private final Collection<File> files; 95 private final GpxLayer gpxLayer; 96 public Loader(Collection<File> files, GpxLayer gpxLayer) { 97 super(tr("Images for {0}", gpxLayer.name)); 98 this.files = files; 99 this.gpxLayer = gpxLayer; 100 } 101 @Override protected void realRun() throws IOException { 102 Main.pleaseWaitDlg.currentAction.setText(tr("Read GPX...")); 103 LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>(); 104 105 // Extract dates and locations from GPX input 106 107 for (GpxTrack trk : gpxLayer.data.tracks) { 108 for (Collection<WayPoint> segment : trk.trackSegs) { 109 for (WayPoint p : segment) { 110 if (!p.attr.containsKey("time")) 111 throw new IOException(tr("No time for point {0} x {1}",p.latlon.lat(),p.latlon.lon())); 112 Date d = null; 113 try { 114 d = DateParser.parse((String) p.attr.get("time")); 115 } catch (ParseException e) { 116 throw new IOException(tr("Cannot read time \"{0}\" from point {1} x {2}",p.attr.get("time"),p.latlon.lat(),p.latlon.lon())); 117 } 118 gps.add(new TimedPoint(d, p.eastNorth)); 119 } 120 } 121 } 122 123 if (gps.isEmpty()) { 124 errorMessage = tr("No images with readable timestamps found."); 125 return; 126 } 127 128 // read the image files 129 ArrayList<ImageEntry> data = new ArrayList<ImageEntry>(files.size()); 130 int i = 0; 131 Main.pleaseWaitDlg.progress.setMaximum(files.size()); 132 for (File f : files) { 133 if (cancelled) 134 break; 135 Main.pleaseWaitDlg.currentAction.setText(tr("Reading {0}...",f.getName())); 136 Main.pleaseWaitDlg.progress.setValue(i++); 137 138 ImageEntry e = new ImageEntry(); 139 try { 140 e.time = ExifReader.readTime(f); 141 } catch (ParseException e1) { 142 continue; 143 } 144 if (e.time == null) 145 continue; 146 e.image = f; 147 e.icon = loadScaledImage(f, 16); 148 149 data.add(e); 150 } 151 layer = new GeoImageLayer(data, gps); 152 layer.calculatePosition(); 153 } 154 @Override protected void finish() { 155 if (layer != null) 156 Main.main.addLayer(layer); 157 } 158 @Override protected void cancel() {cancelled = true;} 159 } 160 161 public ArrayList<ImageEntry> data; 162 private LinkedList<TimedPoint> gps = new LinkedList<TimedPoint>(); 163 164 /** 165 * The delta added to all timestamps in files from the camera 166 * to match to the timestamp from the gps receivers tracklog. 167 */ 168 private long delta = Long.parseLong(Main.pref.get("tagimages.delta", "0")); 169 private long gpstimezone = Long.parseLong(Main.pref.get("tagimages.gpstimezone", "0"))*60*60*1000; 170 private boolean mousePressed = false; 171 private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 172 private MouseAdapter mouseAdapter; 173 private int currentImage; 174 175 public static final class GpsTimeIncorrect extends Exception { 176 public GpsTimeIncorrect(String message, Throwable cause) { 177 super(message, cause); 178 } 179 public GpsTimeIncorrect(String message) { 180 super(message); 181 } 182 } 183 184 private static final class TimedPoint implements Comparable<TimedPoint> { 185 Date time; 186 EastNorth pos; 187 public TimedPoint(Date time, EastNorth pos) { 188 this.time = time; 189 this.pos = pos; 190 } 191 public int compareTo(TimedPoint point) { 192 return time.compareTo(point.time); 193 } 194 } 195 196 public static void create(Collection<File> files, GpxLayer gpxLayer) { 197 Loader loader = new Loader(files, gpxLayer); 198 Main.worker.execute(loader); 199 } 200 201 private GeoImageLayer(final ArrayList<ImageEntry> data, LinkedList<TimedPoint> gps) { 202 super(tr("Geotagged Images")); 203 Collections.sort(data); 204 Collections.sort(gps); 205 this.data = data; 206 this.gps = gps; 207 mouseAdapter = new MouseAdapter(){ 208 @Override public void mousePressed(MouseEvent e) { 209 if (e.getButton() != MouseEvent.BUTTON1) 210 return; 211 mousePressed = true; 212 if (visible) 213 Main.map.mapView.repaint(); 214 } 215 @Override public void mouseReleased(MouseEvent ev) { 216 if (ev.getButton() != MouseEvent.BUTTON1) 217 return; 218 mousePressed = false; 219 if (!visible) 220 return; 221 for (int i = data.size(); i > 0; --i) { 222 ImageEntry e = data.get(i-1); 223 if (e.pos == null) 224 continue; 225 Point p = Main.map.mapView.getPoint(e.pos); 226 Rectangle r = new Rectangle(p.x-e.icon.getIconWidth()/2, p.y-e.icon.getIconHeight()/2, e.icon.getIconWidth(), e.icon.getIconHeight()); 227 if (r.contains(ev.getPoint())) { 228 showImage(i-1); 229 break; 230 } 231 } 232 Main.map.mapView.repaint(); 233 } 234 }; 235 Main.map.mapView.addMouseListener(mouseAdapter); 236 Layer.listeners.add(new LayerChangeListener(){ 237 public void activeLayerChange(Layer oldLayer, Layer newLayer) {} 238 public void layerAdded(Layer newLayer) {} 239 public void layerRemoved(Layer oldLayer) { 240 Main.map.mapView.removeMouseListener(mouseAdapter); 241 } 242 }); 243 } 244 245 private void showImage(int i) { 246 currentImage = i; 247 final JPanel p = new JPanel(new BorderLayout()); 248 final ImageEntry e = data.get(currentImage); 249 final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(e.image, 580))); 250 final JViewport vp = scroll.getViewport(); 251 p.add(scroll, BorderLayout.CENTER); 252 253 final JToggleButton scale = new JToggleButton(ImageProvider.get("dialogs", "zoom-best-fit")); 254 final JButton next = new JButton(ImageProvider.get("dialogs", "next")); 255 final JButton prev = new JButton(ImageProvider.get("dialogs", "previous")); 256 final JToggleButton cent = new JToggleButton(ImageProvider.get("dialogs", "centreview")); 257 258 JPanel p2 = new JPanel(); 259 p2.add(prev); 260 p2.add(scale); 261 p2.add(cent); 262 p2.add(next); 263 prev.setEnabled(currentImage>0?true:false); 264 next.setEnabled(currentImage<data.size()-1?true:false); 265 p.add(p2, BorderLayout.SOUTH); 266 final JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE); 267 final JDialog dlg = pane.createDialog(Main.parent, e.image+" ("+e.coor.toDisplayString()+")"); 268 scale.addActionListener(new ActionListener(){ 269 public void actionPerformed(ActionEvent ev) { 270 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 271 if (scale.getModel().isSelected()) 272 ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight()))); 273 else 274 ((JLabel)vp.getView()).setIcon(new ImageIcon(e.image.getPath())); 275 p.setCursor(Cursor.getDefaultCursor()); 276 } 277 }); 278 scale.setSelected(true); 279 cent.addActionListener(new ActionListener(){ 280 public void actionPerformed(ActionEvent ev) { 281 final ImageEntry e = data.get(currentImage); 282 if (cent.getModel().isSelected()) 283 Main.map.mapView.zoomTo(e.pos, Main.map.mapView.getScale()); 284 } 285 }); 286 287 ActionListener nextprevAction = new ActionListener(){ 288 public void actionPerformed(ActionEvent ev) { 289 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 290 if (ev.getActionCommand().equals("Next")) { 291 currentImage++; 292 if(currentImage>=data.size()-1) next.setEnabled(false); 293 prev.setEnabled(true); 294 } else { 295 currentImage--; 296 if(currentImage<=0) prev.setEnabled(false); 297 next.setEnabled(true); 298 } 299 300 final ImageEntry e = data.get(currentImage); 301 if (scale.getModel().isSelected()) 302 ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight()))); 303 else 304 ((JLabel)vp.getView()).setIcon(new ImageIcon(e.image.getPath())); 305 dlg.setTitle(e.image+" ("+e.coor.toDisplayString()+")"); 306 if (cent.getModel().isSelected()) 307 Main.map.mapView.zoomTo(e.pos, Main.map.mapView.getScale()); 308 p.setCursor(Cursor.getDefaultCursor()); 309 } 310 }; 311 next.setActionCommand("Next"); 312 prev.setActionCommand("Previous"); 313 next.setMnemonic(KeyEvent.VK_RIGHT); 314 prev.setMnemonic(KeyEvent.VK_LEFT); 315 scale.setMnemonic(KeyEvent.VK_F); 316 cent.setMnemonic(KeyEvent.VK_C); 317 next.setToolTipText("Show next image"); 318 prev.setToolTipText("Show previous image"); 319 cent.setToolTipText("Centre image location in main display"); 320 scale.setToolTipText("Scale image to fit"); 321 322 prev.addActionListener(nextprevAction); 323 next.addActionListener(nextprevAction); 324 cent.setSelected(false); 325 326 dlg.addComponentListener(new ComponentListener() { 327 boolean ignoreEvent = true; 328 public void componentHidden(ComponentEvent e) {} 329 public void componentMoved(ComponentEvent e) {} 330 public void componentResized(ComponentEvent ev) { 331 // we ignore the first resize event, as the picture is scaled already on load: 332 if (scale.getModel().isSelected() && !ignoreEvent) { 333 ((JLabel)vp.getView()).setIcon(loadScaledImage(e.image, Math.max(vp.getWidth(), vp.getHeight()))); 334 } 335 ignoreEvent = false; 336 } 337 public void componentShown(ComponentEvent e) {} 338 339 }); 340 dlg.setModal(false); 341 dlg.setVisible(true); 342 dlg.setResizable(true); 343 } 344 345 @Override public Icon getIcon() { 346 return ImageProvider.get("layer", "tagimages_small"); 347 } 348 349 @Override public Object getInfoComponent() { 350 JPanel p = new JPanel(new GridBagLayout()); 351 p.add(new JLabel(getToolTipText()), GBC.eop()); 352 353 p.add(new JLabel(tr("GPS start: {0}",dateFormat.format(gps.getFirst().time))), GBC.eol()); 354 p.add(new JLabel(tr("GPS end: {0}",dateFormat.format(gps.getLast().time))), GBC.eop()); 355 356 p.add(new JLabel(tr("current delta: {0}s",(delta/1000.0))), GBC.eol()); 357 p.add(new JLabel(tr("timezone difference: ")+(gpstimezone>0?"+":"")+(gpstimezone/1000/60/60)), GBC.eop()); 358 359 JList img = new JList(data.toArray()); 360 img.setCellRenderer(new DefaultListCellRenderer(){ 361 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 362 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 363 ImageEntry e = (ImageEntry)value; 364 setIcon(e.icon); 365 setText(e.image.getName()+" ("+dateFormat.format(new Date(e.time.getTime()+(delta+gpstimezone)))+")"); 366 if (e.pos == null) 367 setForeground(Color.red); 368 return this; 369 } 370 }); 371 img.setVisibleRowCount(5); 372 p.add(new JScrollPane(img), GBC.eop().fill(GBC.BOTH)); 373 return p; 374 } 375 376 @Override public String getToolTipText() { 377 int i = 0; 378 for (ImageEntry e : data) 379 if (e.pos != null) 380 i++; 381 return data.size()+" "+trn("image","images",data.size())+". "+tr("{0} within the track.",i); 382 } 383 384 @Override public boolean isMergable(Layer other) { 385 return other instanceof GeoImageLayer; 386 } 387 388 @Override public void mergeFrom(Layer from) { 389 GeoImageLayer l = (GeoImageLayer)from; 390 data.addAll(l.data); 391 } 392 393 @Override public void paint(Graphics g, MapView mv) { 394 boolean clickedFound = false; 395 for (ImageEntry e : data) { 396 if (e.pos != null) { 397 Point p = mv.getPoint(e.pos); 398 Rectangle r = new Rectangle(p.x-e.icon.getIconWidth()/2, p.y-e.icon.getIconHeight()/2, e.icon.getIconWidth(), e.icon.getIconHeight()); 399 e.icon.paintIcon(mv, g, r.x, r.y); 400 Border b = null; 401 Point mousePosition = mv.getMousePosition(); 402 if (mousePosition == null) 403 continue; // mouse outside the whole window 404 if (!clickedFound && mousePressed && r.contains(mousePosition)) { 405 b = BorderFactory.createBevelBorder(BevelBorder.LOWERED); 406 clickedFound = true; 407 } else 408 b = BorderFactory.createBevelBorder(BevelBorder.RAISED); 409 Insets inset = b.getBorderInsets(mv); 410 r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2); 411 b.paintBorder(mv, g, r.x, r.y, r.width, r.height); 412 } 413 } 414 } 415 416 @Override public void visitBoundingBox(BoundingXYVisitor v) { 417 for (ImageEntry e : data) 418 v.visit(e.pos); 419 } 420 421 @Override public Component[] getMenuEntries() { 422 JMenuItem sync = new JMenuItem(tr("Sync clock"), ImageProvider.get("clock")); 423 sync.addActionListener(new ActionListener(){ 424 public void actionPerformed(ActionEvent e) { 425 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory")); 426 fc.setFileSelectionMode(JFileChooser.FILES_ONLY); 427 fc.setAcceptAllFileFilterUsed(false); 428 fc.setFileFilter(new FileFilter(){ 429 @Override public boolean accept(File f) { 430 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg"); 431 } 432 @Override public String getDescription() { 433 return tr("JPEG images (*.jpg)"); 434 } 435 }); 436 fc.showOpenDialog(Main.parent); 437 File sel = fc.getSelectedFile(); 438 if (sel == null) 439 return; 440 Main.pref.put("tagimages.lastdirectory", sel.getPath()); 441 sync(sel); 442 Main.map.repaint(); 443 } 444 }); 445 return new Component[]{ 446 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 447 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 448 new JSeparator(), 449 sync, 450 new JSeparator(), 451 new JMenuItem(new RenameLayerAction(null, this)), 452 new JSeparator(), 453 new JMenuItem(new LayerListPopup.InfoAction(this))}; 454 } 455 456 private void calculatePosition() { 457 for (ImageEntry e : data) { 458 TimedPoint lastTP = null; 459 for (TimedPoint tp : gps) { 460 Date time = new Date(tp.time.getTime() - (delta+gpstimezone)); 461 if (time.after(e.time) && lastTP != null) { 462 double x = (lastTP.pos.east()+tp.pos.east())/2; 463 double y = (lastTP.pos.north()+tp.pos.north())/2; 464 e.pos = new EastNorth(x,y); 465 break; 466 } 467 lastTP = tp; 468 } 469 if (e.pos != null) 470 e.coor = Main.proj.eastNorth2latlon(e.pos); 471 } 472 } 473 474 private void sync(File f) { 475 Date exifDate; 476 try { 477 exifDate = ExifReader.readTime(f); 478 } catch (ParseException e) { 479 JOptionPane.showMessageDialog(Main.parent, tr("The date in file \"{0}\" could not be parsed.", f.getName())); 480 return; 481 } 482 if (exifDate == null) { 483 JOptionPane.showMessageDialog(Main.parent, tr("There is no EXIF time within the file \"{0}\".", f.getName())); 484 return; 485 } 486 JPanel p = new JPanel(new GridBagLayout()); 487 p.add(new JLabel(tr("Image")), GBC.eol()); 488 p.add(new JLabel(loadScaledImage(f, 300)), GBC.eop()); 489 p.add(new JLabel(tr("Enter shown date (mm/dd/yyyy HH:MM:SS)")), GBC.eol()); 490 JTextField gpsText = new JTextField(dateFormat.format(new Date(exifDate.getTime()+delta))); 491 p.add(gpsText, GBC.eol().fill(GBC.HORIZONTAL)); 492 p.add(new JLabel(tr("GPS unit timezone (difference to photo)")), GBC.eol()); 493 String t = Main.pref.get("tagimages.gpstimezone", "0"); 494 if (t.charAt(0) != '-') 495 t = "+"+t; 496 JTextField gpsTimezone = new JTextField(t); 497 p.add(gpsTimezone, GBC.eol().fill(GBC.HORIZONTAL)); 498 499 while (true) { 500 int answer = JOptionPane.showConfirmDialog(Main.parent, p, tr("Syncronize Time with GPS Unit"), JOptionPane.OK_CANCEL_OPTION); 501 if (answer != JOptionPane.OK_OPTION || gpsText.getText().equals("")) 502 return; 503 try { 504 delta = DateParser.parse(gpsText.getText()).getTime() - exifDate.getTime(); 505 String time = gpsTimezone.getText(); 506 if (!time.equals("") && time.charAt(0) == '+') 507 time = time.substring(1); 508 if (time.equals("")) 509 time = "0"; 510 gpstimezone = Long.valueOf(time)*60*60*1000; 511 Main.pref.put("tagimages.delta", ""+delta); 512 Main.pref.put("tagimages.gpstimezone", time); 513 calculatePosition(); 514 return; 515 } catch (NumberFormatException x) { 516 JOptionPane.showMessageDialog(Main.parent, tr("Time entered could not be parsed.")); 517 } catch (ParseException x) { 518 JOptionPane.showMessageDialog(Main.parent, tr("Time entered could not be parsed.")); 519 } 520 } 521 } 522 523 private static Icon loadScaledImage(File f, int maxSize) { 524 Image img = new ImageIcon(f.getPath()).getImage(); 525 int w = img.getWidth(null); 526 int h = img.getHeight(null); 527 if (w>h) { 528 h = Math.round(maxSize*((float)h/w)); 529 w = maxSize; 530 } else { 531 w = Math.round(maxSize*((float)w/h)); 532 h = maxSize; 533 } 534 return new ImageIcon(createResizedCopy(img, w, h)); 535 } 536 537 private static BufferedImage createResizedCopy(Image originalImage, 538 int scaledWidth, int scaledHeight) 539 { 540 BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB); 541 Graphics2D g = scaledBI.createGraphics(); 542 543 g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null); 544 g.dispose(); 545 return scaledBI; 546 } 547 547 } -
trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
r1165 r1169 80 80 81 81 public class GpxLayer extends Layer { 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 82 public GpxData data; 83 private final GpxLayer me; 84 protected static final double PHI = Math.toRadians(15); 85 private boolean computeCacheInSync; 86 private int computeCacheMaxLineLengthUsed; 87 private Color computeCacheColorUsed; 88 private boolean computeCacheColored; 89 90 public GpxLayer(GpxData d) { 91 super((String) d.attr.get("name")); 92 data = d; 93 me = this; 94 computeCacheInSync = false; 95 } 96 97 public GpxLayer(GpxData d, String name) { 98 this(d); 99 this.name = name; 100 } 101 102 @Override public Icon getIcon() { 103 return ImageProvider.get("layer", "gpx_small"); 104 } 105 106 @Override public Object getInfoComponent() { 107 return getToolTipText(); 108 } 109 110 @Override public Component[] getMenuEntries() { 111 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment")); 112 line.addActionListener(new ActionListener() { 113 public void actionPerformed(ActionEvent e) { 114 JRadioButton[] r = new JRadioButton[3]; 115 r[0] = new JRadioButton(tr("Use global settings.")); 116 r[1] = new JRadioButton(tr("Draw lines between points for this layer.")); 117 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer.")); 118 ButtonGroup group = new ButtonGroup(); 119 Box panel = Box.createVerticalBox(); 120 for (JRadioButton b : r) { 121 group.add(b); 122 panel.add(b); 123 } 124 String propName = "draw.rawgps.lines.layer "+name; 125 if (Main.pref.hasKey(propName)) 126 group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true); 127 else 128 group.setSelected(r[0].getModel(), true); 129 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION); 130 if (answer == JOptionPane.CANCEL_OPTION) 131 return; 132 if (group.getSelection() == r[0].getModel()) 133 Main.pref.put(propName, null); 134 else 135 Main.pref.put(propName, group.getSelection() == r[1].getModel()); 136 Main.map.repaint(); 137 } 138 }); 139 140 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser")); 141 color.putClientProperty("help", "Action/LayerCustomizeColor"); 142 color.addActionListener(new ActionListener() { 143 public void actionPerformed(ActionEvent e) { 144 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray)); 145 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 146 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, 147 JOptionPane.PLAIN_MESSAGE, null, options, options[0]); 148 switch (answer) { 149 case 0: 150 Main.pref.putColor("layer "+name, c.getColor()); 151 break; 152 case 1: 153 return; 154 case 2: 155 Main.pref.putColor("layer "+name, null); 156 break; 157 } 158 Main.map.repaint(); 159 } 160 }); 161 162 JMenuItem markersFromNamedTrackpoints = new JMenuItem(tr("Markers From Named Points"), ImageProvider.get("addmarkers")); 163 markersFromNamedTrackpoints.putClientProperty("help", "Action/MarkersFromNamedPoints"); 164 markersFromNamedTrackpoints.addActionListener(new ActionListener() { 165 public void actionPerformed(ActionEvent e) { 166 GpxData namedTrackPoints = new GpxData(); 167 for (GpxTrack track : data.tracks) 168 for (Collection<WayPoint> seg : track.trackSegs) 169 for (WayPoint point : seg) 170 if (point.attr.containsKey("name") || point.attr.containsKey("desc")) 171 namedTrackPoints.waypoints.add(point); 172 173 MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", name), associatedFile, me); 174 if (ml.data.size() > 0) { 175 Main.main.addLayer(ml); 176 } 177 } 178 }); 179 180 JMenuItem importAudio = new JMenuItem(tr("Import Audio"), ImageProvider.get("importaudio")); 181 importAudio.putClientProperty("help", "ImportAudio"); 182 importAudio.addActionListener(new ActionListener() { 183 public void actionPerformed(ActionEvent e) { 184 String dir = Main.pref.get("markers.lastaudiodirectory"); 185 JFileChooser fc = new JFileChooser(dir); 186 fc.setFileSelectionMode(JFileChooser.FILES_ONLY); 187 fc.setAcceptAllFileFilterUsed(false); 188 fc.setFileFilter(new FileFilter(){ 189 @Override public boolean accept(File f) { 190 return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav"); 191 } 192 @Override public String getDescription() { 193 return tr("Wave Audio files (*.wav)"); 194 } 195 }); 196 fc.setMultiSelectionEnabled(true); 197 if(fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) { 198 if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir)) 199 Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath()); 200 201 // FIXME: properly support multi-selection here. 202 // Calling importAudio several times just creates N maker layers, which 203 // is sub-optimal. 204 File sel[] = fc.getSelectedFiles(); 205 if(sel != null) 206 for (int i = 0; i < sel.length; i++) 207 importAudio(sel[i]); 208 209 Main.map.repaint(); 210 } 211 } 212 }); 213 214 JMenuItem tagimage = new JMenuItem(tr("Import images"), ImageProvider.get("tagimages")); 215 tagimage.putClientProperty("help", "Action/ImportImages"); 216 tagimage.addActionListener(new ActionListener() { 217 public void actionPerformed(ActionEvent e) { 218 JFileChooser fc = new JFileChooser(Main.pref.get("tagimages.lastdirectory")); 219 fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 220 fc.setMultiSelectionEnabled(true); 221 fc.setAcceptAllFileFilterUsed(false); 222 fc.setFileFilter(new FileFilter() { 223 @Override public boolean accept(File f) { 224 return f.isDirectory() || f.getName().toLowerCase().endsWith(".jpg"); 225 } 226 @Override public String getDescription() { 227 return tr("JPEG images (*.jpg)"); 228 } 229 }); 230 fc.showOpenDialog(Main.parent); 231 File[] sel = fc.getSelectedFiles(); 232 if (sel == null || sel.length == 0) 233 return; 234 LinkedList<File> files = new LinkedList<File>(); 235 addRecursiveFiles(files, sel); 236 Main.pref.put("tagimages.lastdirectory", fc.getCurrentDirectory().getPath()); 237 GeoImageLayer.create(files, GpxLayer.this); 238 } 239 240 private void addRecursiveFiles(LinkedList<File> files, File[] sel) { 241 for (File f : sel) { 242 if (f.isDirectory()) 243 addRecursiveFiles(files, f.listFiles()); 244 else if (f.getName().toLowerCase().endsWith(".jpg")) 245 files.add(f); 246 } 247 } 248 }); 249 250 if (Main.applet) 251 return new Component[] { 252 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 253 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 254 new JSeparator(), 255 color, 256 line, 257 new JMenuItem(new ConvertToDataLayerAction()), 258 new JSeparator(), 259 new JMenuItem(new RenameLayerAction(associatedFile, this)), 260 new JSeparator(), 261 new JMenuItem(new LayerListPopup.InfoAction(this))}; 262 return new Component[] { 263 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 264 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 265 new JSeparator(), 266 new JMenuItem(new SaveAction(this)), 267 new JMenuItem(new SaveAsAction(this)), 268 // new JMenuItem(new UploadTraceAction()), 269 color, 270 line, 271 tagimage, 272 importAudio, 273 markersFromNamedTrackpoints, 274 new JMenuItem(new ConvertToDataLayerAction()), 275 275 new JMenuItem(new DownloadAlongTrackAction()), 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 276 new JSeparator(), 277 new JMenuItem(new RenameLayerAction(associatedFile, this)), 278 new JSeparator(), 279 new JMenuItem(new LayerListPopup.InfoAction(this))}; 280 } 281 282 @Override public String getToolTipText() { 283 StringBuilder info = new StringBuilder().append("<html>"); 284 285 info.append(trn("{0} track, ", "{0} tracks, ", 286 data.tracks.size(), data.tracks.size())).append(trn("{0} route, ", "{0} routes, ", 287 data.routes.size(), data.routes.size())).append(trn("{0} waypoint", "{0} waypoints", 288 data.waypoints.size(), data.waypoints.size())).append("<br>"); 289 290 if (data.attr.containsKey("name")) 291 info.append(tr("Name: {0}", data.attr.get("name"))).append("<br>"); 292 293 if (data.attr.containsKey("desc")) 294 info.append(tr("Description: {0}", data.attr.get("desc"))).append("<br>"); 295 296 if(data.tracks.size() > 0){ 297 boolean first = true; 298 WayPoint earliest = null, latest = null; 299 300 for(GpxTrack trk: data.tracks){ 301 for(Collection<WayPoint> seg:trk.trackSegs){ 302 for(WayPoint pnt:seg){ 303 if(first){ 304 latest = earliest = pnt; 305 first = false; 306 }else{ 307 if(pnt.compareTo(earliest) < 0){ 308 earliest = pnt; 309 }else{ 310 latest = pnt; 311 } 312 } 313 } 314 } 315 } 316 if (earliest != null && latest != null) { 317 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT); 318 info.append(tr("Timespan: ") + df.format(new Date((long)(earliest.time * 1000))) + " - " 319 + df.format(new Date((long)(latest.time * 1000)))); 320 int diff = (int)(latest.time - earliest.time); 321 info.append(" (" + (diff / 3600) + ":" + ((diff % 3600)/60) + ")"); 322 info.append("<br>"); 323 } 324 } 325 info.append(tr("Length: ") + new DecimalFormat("#0.00").format(data.length() / 1000) + "km"); 326 info.append("<br>"); 327 328 return info.append("</html>").toString(); 329 } 330 331 @Override public boolean isMergable(Layer other) { 332 return other instanceof GpxLayer; 333 } 334 335 @Override public void mergeFrom(Layer from) { 336 data.mergeFrom(((GpxLayer)from).data); 337 computeCacheInSync = false; 338 } 339 340 private static Color[] colors = new Color[256]; 341 static { 342 for (int i = 0; i < colors.length; i++) { 343 colors[i] = Color.getHSBColor(i/300.0f, 1, 1); 344 } 345 } 346 347 // lookup array to draw arrows without doing any math 348 private static int ll0 = 9; 349 private static int sl4 = 5; 350 private static int sl9 = 3; 351 private static int[][] dir = { 352 {+sl4,+ll0,+ll0,+sl4}, 353 {-sl9,+ll0,+sl9,+ll0}, 354 {-ll0,+sl4,-sl4,+ll0}, 355 {-ll0,-sl9,-ll0,+sl9}, 356 {-sl4,-ll0,-ll0,-sl4}, 357 {+sl9,-ll0,-sl9,-ll0}, 358 {+ll0,-sl4,+sl4,-ll0}, 359 {+ll0,+sl9,+ll0,-sl9}, 360 {+sl4,+ll0,+ll0,+sl4}, 361 {-sl9,+ll0,+sl9,+ll0}, 362 {-ll0,+sl4,-sl4,+ll0}, 363 {-ll0,-sl9,-ll0,+sl9} 364 }; 365 366 @Override public void paint(Graphics g, MapView mv) { 367 368 /**************************************************************** 369 ********** STEP 1 - GET CONFIG VALUES ************************** 370 ****************************************************************/ 371 // Long startTime = System.currentTimeMillis(); 372 Color neutralColor = Main.pref.getColor(marktr("gps point"), "layer "+name, Color.GRAY); 373 boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force"); // also draw lines between points belonging to different segments 374 boolean direction = Main.pref.getBoolean("draw.rawgps.direction"); // draw direction arrows on the lines 375 int maxLineLength = -1; 376 try { 377 maxLineLength = Integer.parseInt(Main.pref.get("draw.rawgps.max-line-length", "-1")); // don't draw lines if longer than x meters 378 } catch (java.lang.NumberFormatException e) { 379 Main.pref.put("draw.rawgps.max-line-length", "-1"); 380 } 381 boolean lines = Main.pref.getBoolean("draw.rawgps.lines"); // draw line between points, global setting 382 String linesKey = "draw.rawgps.lines.layer "+name; 383 if (Main.pref.hasKey(linesKey)) 384 lines = Main.pref.getBoolean(linesKey); // draw lines, per-layer setting 385 boolean large = Main.pref.getBoolean("draw.rawgps.large"); // paint large dots for points 386 boolean colored = Main.pref.getBoolean("draw.rawgps.colors"); // color the lines 387 boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection"); // paint direction arrow with alternate math. may be faster 388 int delta = 0; 389 try { 390 delta = Integer.parseInt(Main.pref.get("draw.rawgps.min-arrow-distance", "0")); // don't draw arrows nearer to each other than this 391 } catch (java.lang.NumberFormatException e) { 392 Main.pref.put("draw.rawgps.min-arrow-distance", "0"); 393 } 394 395 /**************************************************************** 396 ********** STEP 2a - CHECK CACHE VALIDITY ********************** 397 ****************************************************************/ 398 if (computeCacheInSync && ((computeCacheMaxLineLengthUsed != maxLineLength) || 399 (!neutralColor.equals(computeCacheColorUsed)) || 400 (computeCacheColored != colored))) { 401 401 // System.out.println("(re-)computing gpx line styles, reason: CCIS=" + computeCacheInSync + " CCMLLU=" + (computeCacheMaxLineLengthUsed != maxLineLength) + " CCCU=" + (!neutralColor.equals(computeCacheColorUsed)) + " CCC=" + (computeCacheColored != colored)); 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 402 computeCacheMaxLineLengthUsed = maxLineLength; 403 computeCacheInSync = false; 404 computeCacheColorUsed = neutralColor; 405 computeCacheColored = colored; 406 } 407 408 /**************************************************************** 409 ********** STEP 2b - RE-COMPUTE CACHE DATA ********************* 410 ****************************************************************/ 411 if (!computeCacheInSync) { // don't compute if the cache is good 412 WayPoint oldWp = null; 413 for (GpxTrack trk : data.tracks) { 414 if (!forceLines) { // don't draw lines between segments, unless forced to 415 oldWp = null; 416 } 417 for (Collection<WayPoint> segment : trk.trackSegs) { 418 for (WayPoint trkPnt : segment) { 419 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) { 420 continue; 421 } 422 if (oldWp != null) { 423 double dist = trkPnt.latlon.greatCircleDistance(oldWp.latlon); 424 double dtime = trkPnt.time - oldWp.time; 425 double vel = dist/dtime; 426 427 if (!colored) { 428 trkPnt.speedLineColor = neutralColor; 429 } else if (dtime <= 0 || vel < 0 || vel > 36) { // attn: bad case first 430 trkPnt.speedLineColor = colors[255]; 431 } else { 432 trkPnt.speedLineColor = colors[(int) (7*vel)]; 433 } 434 if (maxLineLength == -1 || dist <= maxLineLength) { 435 trkPnt.drawLine = true; 436 trkPnt.dir = (int)(Math.atan2(-trkPnt.eastNorth.north()+oldWp.eastNorth.north(), trkPnt.eastNorth.east()-oldWp.eastNorth.east()) / Math.PI * 4 + 3.5); // crude but works 437 } else { 438 trkPnt.drawLine = false; 439 } 440 } else { // make sure we reset outdated data 441 trkPnt.speedLineColor = colors[255]; 442 trkPnt.drawLine = false; 443 } 444 oldWp = trkPnt; 445 } 446 } 447 } 448 computeCacheInSync = true; 449 } 450 451 /**************************************************************** 452 ********** STEP 3a - DRAW LINES ******************************** 453 ****************************************************************/ 454 if (lines) { 455 Point old = null; 456 for (GpxTrack trk : data.tracks) { 457 for (Collection<WayPoint> segment : trk.trackSegs) { 458 for (WayPoint trkPnt : segment) { 459 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 460 continue; 461 Point screen = mv.getPoint(trkPnt.eastNorth); 462 if (trkPnt.drawLine) { 463 // skip points that are on the same screenposition 464 if (old != null && ((old.x != screen.x) || (old.y != screen.y))) { 465 g.setColor(trkPnt.speedLineColor); 466 g.drawLine(old.x, old.y, screen.x, screen.y); 467 } 468 } 469 old = screen; 470 } // end for trkpnt 471 } // end for segment 472 } // end for trk 473 } // end if lines 474 475 /**************************************************************** 476 ********** STEP 3b - DRAW NICE ARROWS ************************** 477 ****************************************************************/ 478 if (lines && direction && !alternatedirection) { 479 Point old = null; 480 Point oldA = null; // last arrow painted 481 for (GpxTrack trk : data.tracks) { 482 for (Collection<WayPoint> segment : trk.trackSegs) { 483 for (WayPoint trkPnt : segment) { 484 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 485 continue; 486 if (trkPnt.drawLine) { 487 Point screen = mv.getPoint(trkPnt.eastNorth); 488 // skip points that are on the same screenposition 489 if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) { 490 g.setColor(trkPnt.speedLineColor); 491 double t = Math.atan2(screen.y-old.y, screen.x-old.x) + Math.PI; 492 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t-PHI)), (int)(screen.y 493 + 10*Math.sin(t-PHI))); 494 g.drawLine(screen.x,screen.y, (int)(screen.x + 10*Math.cos(t+PHI)), (int)(screen.y 495 + 10*Math.sin(t+PHI))); 496 oldA = screen; 497 } 498 old = screen; 499 } 500 } // end for trkpnt 501 } // end for segment 502 } // end for trk 503 } // end if lines 504 505 /**************************************************************** 506 ********** STEP 3c - DRAW FAST ARROWS ************************** 507 ****************************************************************/ 508 if (lines && direction && alternatedirection) { 509 Point old = null; 510 Point oldA = null; // last arrow painted 511 for (GpxTrack trk : data.tracks) { 512 for (Collection<WayPoint> segment : trk.trackSegs) { 513 for (WayPoint trkPnt : segment) { 514 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 515 continue; 516 if (trkPnt.drawLine) { 517 Point screen = mv.getPoint(trkPnt.eastNorth); 518 // skip points that are on the same screenposition 519 if (old != null && (oldA == null || screen.x < oldA.x-delta || screen.x > oldA.x+delta || screen.y < oldA.y-delta || screen.y > oldA.y+delta)) { 520 g.setColor(trkPnt.speedLineColor); 521 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y + dir[trkPnt.dir][1]); 522 g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y + dir[trkPnt.dir][3]); 523 oldA = screen; 524 } 525 old = screen; 526 } 527 } // end for trkpnt 528 } // end for segment 529 } // end for trk 530 } // end if lines 531 532 /**************************************************************** 533 ********** STEP 3d - DRAW LARGE POINTS ************************* 534 ****************************************************************/ 535 if (large) { 536 g.setColor(neutralColor); 537 for (GpxTrack trk : data.tracks) { 538 for (Collection<WayPoint> segment : trk.trackSegs) { 539 for (WayPoint trkPnt : segment) { 540 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 541 continue; 542 Point screen = mv.getPoint(trkPnt.eastNorth); 543 g.fillRect(screen.x-1, screen.y-1, 3, 3); 544 } // end for trkpnt 545 } // end for segment 546 } // end for trk 547 } // end if large 548 549 /**************************************************************** 550 ********** STEP 3e - DRAW SMALL POINTS FOR LINES *************** 551 ****************************************************************/ 552 if (!large && lines){ 553 g.setColor(neutralColor); 554 for (GpxTrack trk : data.tracks) { 555 for (Collection<WayPoint> segment : trk.trackSegs) { 556 for (WayPoint trkPnt : segment) { 557 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 558 continue; 559 if (!trkPnt.drawLine) { 560 Point screen = mv.getPoint(trkPnt.eastNorth); 561 g.drawRect(screen.x, screen.y, 0, 0); 562 } 563 } // end for trkpnt 564 } // end for segment 565 } // end for trk 566 } // end if large 567 568 /**************************************************************** 569 ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ******** 570 ****************************************************************/ 571 if (!large && !lines){ 572 g.setColor(neutralColor); 573 for (GpxTrack trk : data.tracks) { 574 for (Collection<WayPoint> segment : trk.trackSegs) { 575 for (WayPoint trkPnt : segment) { 576 if (Double.isNaN(trkPnt.latlon.lat()) || Double.isNaN(trkPnt.latlon.lon())) 577 continue; 578 Point screen = mv.getPoint(trkPnt.eastNorth); 579 g.drawRect(screen.x, screen.y, 0, 0); 580 } // end for trkpnt 581 } // end for segment 582 } // end for trk 583 } // end if large 584 585 //Long duration = System.currentTimeMillis() - startTime; 586 //System.out.println(duration); 587 } // end paint 588 589 @Override public void visitBoundingBox(BoundingXYVisitor v) { 590 for (WayPoint p : data.waypoints) 591 v.visit(p.eastNorth); 592 593 for (GpxRoute rte : data.routes) { 594 Collection<WayPoint> r = rte.routePoints; 595 for (WayPoint p : r) { 596 v.visit(p.eastNorth); 597 } 598 } 599 600 for (GpxTrack trk : data.tracks) { 601 for (Collection<WayPoint> seg : trk.trackSegs) { 602 for (WayPoint p : seg) { 603 v.visit(p.eastNorth); 604 } 605 } 606 } 607 } 608 609 public class UploadTraceAction extends AbstractAction { 610 public UploadTraceAction() { 611 super(tr("Upload this trace..."), ImageProvider.get("uploadtrace")); 612 } 613 public void actionPerformed(ActionEvent e) { 614 JPanel msg = new JPanel(new GridBagLayout()); 615 msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+ 616 "use with care and check if it works as expected.</html>")), GBC.eop()); 617 ButtonGroup bg = new ButtonGroup(); 618 JRadioButton c1 = null; 619 JRadioButton c2 = null; 620 621 //TODO 622 //check whether data comes from server 623 //check whether data changed sind last save/open 624 625 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true); 626 c2 = new JRadioButton(tr("Upload raw file: "), false); 627 c2.setEnabled(false); 628 c1.setEnabled(false); 629 bg.add(c1); 630 bg.add(c2); 631 632 msg.add(c1, GBC.eol()); 633 msg.add(c2, GBC.eop()); 634 635 636 JLabel description = new JLabel((String) data.attr.get("desc")); 637 JTextField tags = new JTextField(); 638 tags.setText((String) data.attr.get("keywords")); 639 msg.add(new JLabel(tr("Description:")), GBC.std()); 640 msg.add(description, GBC.eol().fill(GBC.HORIZONTAL)); 641 msg.add(new JLabel(tr("Tags (keywords in GPX):")), GBC.std()); 642 msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL)); 643 JCheckBox c3 = new JCheckBox("public"); 644 msg.add(c3, GBC.eop()); 645 msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop()); 646 647 int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION); 648 if (answer == JOptionPane.OK_OPTION) 649 { 650 try { 651 String version = Main.pref.get("osm-server.version", "0.5"); 652 URL url = new URL(Main.pref.get("osm-server.url") + "/" + version + "/gpx/create"); 653 654 // create a boundary string 655 String boundary = MultiPartFormOutputStream.createBoundary(); 656 URLConnection urlConn = MultiPartFormOutputStream.createConnection(url); 657 urlConn.setRequestProperty("Accept", "*/*"); 658 urlConn.setRequestProperty("Content-Type", MultiPartFormOutputStream.getContentType(boundary)); 659 // set some other request headers... 660 urlConn.setRequestProperty("Connection", "Keep-Alive"); 661 urlConn.setRequestProperty("Cache-Control", "no-cache"); 662 // no need to connect cuz getOutputStream() does it 663 MultiPartFormOutputStream out = new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary); 664 out.writeField("description", description.getText()); 665 out.writeField("tags", tags.getText()); 666 out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0"); 667 // upload a file 668 // out.writeFile("gpx_file", "text/xml", associatedFile); 669 // can also write bytes directly 670 // out.writeFile("myFile", "text/plain", "C:\\test.txt", 671 // "This is some file text.".getBytes("ASCII")); 672 File tmp = File.createTempFile("josm", "tmp.gpx"); 673 FileOutputStream outs = new FileOutputStream(tmp); 674 new GpxWriter(outs).write(data); 675 outs.close(); 676 FileInputStream ins = new FileInputStream(tmp); 677 new GpxWriter(System.out).write(data); 678 out.writeFile("gpx_file", "text/xml", data.storageFile.getName(), ins); 679 out.close(); 680 tmp.delete(); 681 // read response from server 682 BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); 683 String line = ""; 684 while((line = in.readLine()) != null) { 685 System.out.println(line); 686 } 687 in.close(); 688 689 //TODO check response 690 /* int retCode = urlConn.getResponseCode(); 691 System.out.println("got return: " + retCode); 692 String retMsg = urlConn.getResponseMessage(); 693 urlConn.disconnect(); 694 if (retCode != 200) { 695 // Look for a detailed error message from the server 696 if (urlConn.getHeaderField("Error") != null) 697 retMsg += "\n" + urlConn.getHeaderField("Error"); 698 699 // Report our error 700 ByteArrayOutputStream o = new ByteArrayOutputStream(); 701 System.out.println(new String(o.toByteArray(), "UTF-8").toString()); 702 throw new RuntimeException(retCode+" "+retMsg); 703 } 704 */ 705 } catch (UnknownHostException ex) { 706 throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex); 707 } catch (Exception ex) { 708 //if (cancel) 709 // return; // assume cancel 710 if (ex instanceof RuntimeException) 711 throw (RuntimeException)ex; 712 throw new RuntimeException(ex.getMessage(), ex); 713 } 714 } 715 } 716 } 717 718 public class ConvertToDataLayerAction extends AbstractAction { 719 public ConvertToDataLayerAction() { 720 super(tr("Convert to data layer"), ImageProvider.get("converttoosm")); 721 } 722 public void actionPerformed(ActionEvent e) { 723 JPanel msg = new JPanel(new GridBagLayout()); 724 msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol()); 725 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop()); 726 if (!DontShowAgainInfo.show("convert_to_data", msg)) 727 return; 728 DataSet ds = new DataSet(); 729 for (GpxTrack trk : data.tracks) { 730 for (Collection<WayPoint> segment : trk.trackSegs) { 731 Way w = new Way(); 732 for (WayPoint p : segment) { 733 Node n = new Node(p.latlon); 734 String timestr = p.getString("time"); 735 if(timestr != null) 736 { 737 timestr = timestr.replace("Z","+00:00"); 738 n.timestamp = timestr; 739 } 740 ds.nodes.add(n); 741 w.nodes.add(n); 742 } 743 ds.ways.add(w); 744 } 745 } 746 Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", GpxLayer.this.name), null)); 747 Main.main.removeLayer(GpxLayer.this); 748 } 749 } 750 750 751 751 /** 752 752 * Action that issues a series of download requests to the API, following the GPX track. 753 * 753 * 754 754 * @author fred 755 755 */ … … 762 762 JList buffer = new JList(new String[] { "50 metres", "500 metres", "5000 metres" }); 763 763 JList maxRect = new JList(new String[] { "1 sq km", "5 sq km", "10 sq km", "20 sq km" }); 764 764 765 765 msg.add(new JLabel(tr("Download everything within:")), GBC.eol()); 766 766 msg.add(buffer, GBC.eol()); 767 767 msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol()); 768 768 msg.add(maxRect, GBC.eol()); 769 770 if (JOptionPane.showConfirmDialog(Main.parent, msg, 771 tr("Download from OSM along this track"), 769 770 if (JOptionPane.showConfirmDialog(Main.parent, msg, 771 tr("Download from OSM along this track"), 772 772 JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { 773 773 return; 774 774 775 775 } 776 776 … … 781 781 double latsum = 0; 782 782 int latcnt = 0; 783 783 784 784 for (GpxTrack trk : data.tracks) { 785 785 for (Collection<WayPoint> segment : trk.trackSegs) { … … 790 790 } 791 791 } 792 792 793 793 double avglat = latsum / latcnt; 794 794 double scale = Math.cos(Math.toRadians(avglat)); … … 797 797 * Compute buffer zone extents and maximum bounding box size. Note how the 798 798 * maximum we ever offer is a bbox area of 0.002, while the API theoretically 799 * supports 0.25, but as soon as you touch any built-up area, that kind of 800 * bounding box will download forever and then stop because it has more than 799 * supports 0.25, but as soon as you touch any built-up area, that kind of 800 * bounding box will download forever and then stop because it has more than 801 801 * 50k nodes. 802 802 */ … … 809 809 } 810 810 buffer_x = buffer_y / scale; 811 811 812 812 double max_area; 813 813 switch(maxRect.getSelectedIndex()) { … … 820 820 Area a = new Area(); 821 821 Rectangle2D r = new Rectangle2D.Double(); 822 822 823 823 /* 824 824 * Collect the combined area of all gpx points plus buffer zones around them. … … 836 836 } 837 837 } 838 838 839 839 /* 840 840 * Area "a" now contains the hull that we would like to download data for. … … 858 858 * actually has something in it. 859 859 */ 860 860 861 861 List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>(); 862 862 863 863 addToDownload(a, a.getBounds(), toDownload, max_area); 864 864 865 865 msg = new JPanel(new GridBagLayout()); 866 866 867 867 msg.add(new JLabel(tr("<html>This action will require {0} individual<br>download requests. Do you wish<br>to continue?</html>", 868 868 toDownload.size())), GBC.eol()); 869 870 if (JOptionPane.showConfirmDialog(Main.parent, msg, 871 tr("Download from OSM along this track"), 869 870 if (JOptionPane.showConfirmDialog(Main.parent, msg, 871 tr("Download from OSM along this track"), 872 872 JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { 873 873 return; 874 874 } 875 875 876 876 // FIXME: DownloadTask's "please wait" dialog should display the number of 877 877 // downloads left, and "cancel" needs to be honoured. An error along the way … … 883 883 } 884 884 } 885 885 886 886 private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) { 887 887 Area tmp = new Area(r); … … 909 909 } 910 910 } 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 911 912 /** 913 * Makes a new marker layer derived from this GpxLayer containing at least one 914 * audio marker which the given audio file is associated with. 915 * Markers are derived from the following 916 * (a) explict waypoints in the GPX layer, or 917 * (b) named trackpoints in the GPX layer, or 918 * (c) (in future) voice recognised markers in the sound recording 919 * (d) a single marker at the beginning of the track 920 * @param wavFile : the file to be associated with the markers in the new marker layer 921 */ 922 private void importAudio(File wavFile) { 923 String uri = "file:".concat(wavFile.getAbsolutePath()); 924 MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name), associatedFile, me); 925 926 Collection<WayPoint> waypoints = new ArrayList<WayPoint>(); 927 boolean timedMarkersOmitted = false; 928 boolean untimedMarkersOmitted = false; 929 double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /* about 25m */ 930 931 // determine time of first point in track 932 double firstTime = -1.0; 933 if (data.tracks != null && ! data.tracks.isEmpty()) { 934 for (GpxTrack track : data.tracks) { 935 if (track.trackSegs == null) continue; 936 for (Collection<WayPoint> seg : track.trackSegs) { 937 for (WayPoint w : seg) { 938 firstTime = w.time; 939 break; 940 } 941 if (firstTime >= 0.0) break; 942 } 943 if (firstTime >= 0.0) break; 944 } 945 } 946 if (firstTime < 0.0) { 947 JOptionPane.showMessageDialog(Main.parent, tr("No GPX track available in layer to associate audio with.")); 948 return; 949 } 950 951 // (a) try explicit timestamped waypoints - unless suppressed 952 if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) && 953 data.waypoints != null && ! data.waypoints.isEmpty()) 954 { 955 for (WayPoint w : data.waypoints) { 956 if (w.time > firstTime) { 957 waypoints.add(w); 958 } else if (w.time > 0.0) { 959 timedMarkersOmitted = true; 960 } 961 } 962 } 963 964 // (b) try explicit waypoints without timestamps - unless suppressed 965 if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) && 966 data.waypoints != null && ! data.waypoints.isEmpty()) 967 { 968 for (WayPoint w : data.waypoints) { 969 if (waypoints.contains(w)) { continue; } 970 WayPoint wNear = nearestPointOnTrack(w.eastNorth, snapDistance); 971 if (wNear != null) { 972 WayPoint wc = new WayPoint(w.latlon); 973 wc.time = wNear.time; 974 if (w.attr.containsKey("name")) wc.attr.put("name", w.getString("name")); 975 waypoints.add(wc); 976 } else { 977 untimedMarkersOmitted = true; 978 } 979 } 980 } 981 982 // (c) use explicitly named track points, again unless suppressed 983 if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) && 984 data.tracks != null && ! data.tracks.isEmpty()) 985 { 986 for (GpxTrack track : data.tracks) { 987 if (track.trackSegs == null) continue; 988 for (Collection<WayPoint> seg : track.trackSegs) { 989 for (WayPoint w : seg) { 990 if (w.attr.containsKey("name") || w.attr.containsKey("desc")) { 991 waypoints.add(w); 992 } 993 } 994 } 995 } 996 } 997 998 // (d) analyse audio for spoken markers here, in due course 999 1000 // (e) simply add a single marker at the start of the track 1001 if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && 1002 data.tracks != null && ! data.tracks.isEmpty()) 1003 { 1004 boolean gotOne = false; 1005 for (GpxTrack track : data.tracks) { 1006 if (track.trackSegs == null) continue; 1007 for (Collection<WayPoint> seg : track.trackSegs) { 1008 for (WayPoint w : seg) { 1009 WayPoint wStart = new WayPoint(w.latlon); 1010 wStart.attr.put("name", "start"); 1011 wStart.time = w.time; 1012 waypoints.add(wStart); 1013 gotOne = true; 1014 break; 1015 } 1016 if (gotOne) break; 1017 } 1018 if (gotOne) break; 1019 } 1020 } 1021 1022 /* we must have got at least one waypoint now */ 1023 1024 Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() { 1025 public int compare(WayPoint a, WayPoint b) { 1026 return a.time <= b.time ? -1 : 1; 1027 } 1028 }); 1029 1030 firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */ 1031 for (WayPoint w : waypoints) { 1032 if (firstTime < 0.0) firstTime = w.time; 1033 double offset = w.time - firstTime; 1034 String name; 1035 if (w.attr.containsKey("name")) 1036 name = w.getString("name"); 1037 else if (w.attr.containsKey("desc")) 1038 name = w.getString("desc"); 1039 else 1040 name = AudioMarker.inventName(offset); 1041 AudioMarker am = AudioMarker.create(w.latlon, 1042 name, uri, ml, w.time, offset); 1043 ml.data.add(am); 1044 } 1045 Main.main.addLayer(ml); 1046 1047 if (timedMarkersOmitted) { 1048 JOptionPane.showMessageDialog(Main.parent, 1049 tr("Some waypoints with timestamps from before the start of the track were omitted.")); 1050 } 1051 if (untimedMarkersOmitted) { 1052 JOptionPane.showMessageDialog(Main.parent, 1053 tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted.")); 1054 } 1055 } 1056 1057 /** 1058 * Makes a WayPoint at the projection of point P onto the track providing P is 1059 * less than tolerance away from the track 1060 1061 * @param P : the point to determine the projection for 1062 * @param tolerance : must be no further than this from the track 1063 * @return the closest point on the track to P, which may be the 1064 * first or last point if off the end of a segment, or may be null if 1065 * nothing close enough 1066 */ 1067 public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) { 1068 /* 1069 * assume the coordinates of P are xp,yp, and those of a section of track 1070 * between two trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point. 1071 * 1072 * The equation of RS is Ax + By + C = 0 where 1073 * A = ys - yr 1074 * B = xr - xs 1075 * C = - Axr - Byr 1076 * 1077 * Also, note that the distance RS^2 is A^2 + B^2 1078 * 1079 * If RS^2 == 0.0 ignore the degenerate section of track 1080 * 1081 * PN^2 = (Axp + Byp + C)^2 / RS^2 1082 * that is the distance from P to the line 1083 * 1084 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject 1085 * the line; otherwise... 1086 * determine if the projected poijnt lies within the bounds of the line: 1087 * PR^2 - PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2 1088 * 1089 * where PR^2 = (xp - xr)^2 + (yp-yr)^2 1090 * and PS^2 = (xp - xs)^2 + (yp-ys)^2 1091 * 1092 * If so, calculate N as 1093 * xn = xr + (RN/RS) B 1094 * yn = y1 + (RN/RS) A 1095 * 1096 * where RN = sqrt(PR^2 - PN^2) 1097 */ 1098 1099 double PNminsq = tolerance * tolerance; 1100 EastNorth bestEN = null; 1101 double bestTime = 0.0; 1102 double px = P.east(); 1103 double py = P.north(); 1104 double rx = 0.0, ry = 0.0, sx, sy, x, y; 1105 if (data.tracks == null) return null; 1106 for (GpxTrack track : data.tracks) { 1107 if (track.trackSegs == null) continue; 1108 for (Collection<WayPoint> seg : track.trackSegs) { 1109 WayPoint R = null; 1110 for (WayPoint S : seg) { 1111 if (R == null) { 1112 R = S; 1113 rx = R.eastNorth.east(); 1114 ry = R.eastNorth.north(); 1115 x = px - rx; 1116 y = py - ry; 1117 double PRsq = x * x + y * y; 1118 if (PRsq < PNminsq) { 1119 PNminsq = PRsq; 1120 bestEN = R.eastNorth; 1121 bestTime = R.time; 1122 } 1123 } else { 1124 sx = S.eastNorth.east(); 1125 sy = S.eastNorth.north(); 1126 double A = sy - ry; 1127 double B = rx - sx; 1128 double C = - A * rx - B * ry; 1129 double RSsq = A * A + B * B; 1130 if (RSsq == 0.0) continue; 1131 double PNsq = A * px + B * py + C; 1132 PNsq = PNsq * PNsq / RSsq; 1133 if (PNsq < PNminsq) { 1134 x = px - rx; 1135 y = py - ry; 1136 double PRsq = x * x + y * y; 1137 x = px - sx; 1138 y = py - sy; 1139 double PSsq = x * x + y * y; 1140 if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) { 1141 double RNoverRS = Math.sqrt((PRsq - PNsq)/RSsq); 1142 double nx = rx - RNoverRS * B; 1143 double ny = ry + RNoverRS * A; 1144 bestEN = new EastNorth(nx, ny); 1145 bestTime = R.time + RNoverRS * (S.time - R.time); 1146 PNminsq = PNsq; 1147 } 1148 } 1149 R = S; 1150 rx = sx; 1151 ry = sy; 1152 } 1153 } 1154 if (R != null) { 1155 /* if there is only one point in the seg, it will do this twice, but no matter */ 1156 rx = R.eastNorth.east(); 1157 ry = R.eastNorth.north(); 1158 x = px - rx; 1159 y = py - ry; 1160 double PRsq = x * x + y * y; 1161 if (PRsq < PNminsq) { 1162 PNminsq = PRsq; 1163 bestEN = R.eastNorth; 1164 bestTime = R.time; 1165 } 1166 } 1167 } 1168 } 1169 if (bestEN == null) return null; 1170 WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN)); 1171 best.time = bestTime; 1172 return best; 1173 } 1174 1174 } -
trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
r989 r1169 17 17 /** 18 18 * A layer encapsulates the gui componente of one dataset and its representation. 19 * 20 * Some layers may display data directly importet from OSM server. Other only 21 * display background images. Some can be edited, some not. Some are static and 19 * 20 * Some layers may display data directly importet from OSM server. Other only 21 * display background images. Some can be edited, some not. Some are static and 22 22 * other changes dynamically (auto-updated). 23 23 * 24 24 * Layers can be visible or not. Most actions the user can do applies only on 25 25 * selected layers. The available actions depend on the selected layers too. 26 * 27 * All layers are managed by the MapView. They are displayed in a list to the 26 * 27 * All layers are managed by the MapView. They are displayed in a list to the 28 28 * right of the screen. 29 * 29 * 30 30 * @author imi 31 31 */ 32 32 abstract public class Layer implements Destroyable, MapViewPaintable { 33 33 34 35 36 37 38 39 40 41 42 34 /** 35 * Interface to notify listeners of the change of the active layer. 36 * @author imi 37 */ 38 public interface LayerChangeListener { 39 void activeLayerChange(Layer oldLayer, Layer newLayer); 40 void layerAdded(Layer newLayer); 41 void layerRemoved(Layer oldLayer); 42 } 43 43 44 45 46 47 48 44 /** 45 * The listener of the active layer changes. You may register/deregister yourself 46 * while an LayerChangeListener - action is executed. 47 */ 48 public static final Collection<LayerChangeListener> listeners = new CopyOnWriteArrayList<LayerChangeListener>(); 49 49 50 51 52 53 50 /** 51 * The visibility state of the layer. 52 */ 53 public boolean visible = true; 54 54 55 56 57 58 55 /** 56 * The layer should be handled as a background layer in automatic handling 57 */ 58 public boolean background = false; 59 59 60 61 62 63 64 65 66 67 60 /** 61 * The name of this layer. 62 */ 63 public String name; 64 /** 65 * If a file is associated with this layer, this variable should be set to it. 66 */ 67 public File associatedFile; 68 68 69 70 71 72 73 74 69 /** 70 * Create the layer and fill in the necessary components. 71 */ 72 public Layer(String name) { 73 this.name = name; 74 } 75 75 76 77 78 79 80 81 82 * Return a representative small image for this layer. The image must not 83 84 85 76 /** 77 * Paint the dataset using the engine set. 78 * @param mv The object that can translate GeoPoints to screen coordinates. 79 */ 80 abstract public void paint(Graphics g, MapView mv); 81 /** 82 * Return a representative small image for this layer. The image must not 83 * be larger than 64 pixel in any dimension. 84 */ 85 abstract public Icon getIcon(); 86 86 87 88 89 90 87 /** 88 * @return A small tooltip hint about some statistics for this layer. 89 */ 90 abstract public String getToolTipText(); 91 91 92 /** 93 * Merges the given layer into this layer. Throws if the layer types are 94 * incompatible. 95 * @param from The layer that get merged into this one. After the merge, 96 * the other layer is not usable anymore and passing to one others 97 * mergeFrom should be one of the last things to do with a layer. 98 */ 99 abstract public void mergeFrom(Layer from); 100 101 /** 102 * @param other The other layer that is tested to be mergable with this. 103 * @return Whether the other layer can be merged into this layer. 104 */ 105 abstract public boolean isMergable(Layer other); 92 /** 93 * Merges the given layer into this layer. Throws if the layer types are 94 * incompatible. 95 * @param from The layer that get merged into this one. After the merge, 96 * the other layer is not usable anymore and passing to one others 97 * mergeFrom should be one of the last things to do with a layer. 98 */ 99 abstract public void mergeFrom(Layer from); 106 100 107 abstract public void visitBoundingBox(BoundingXYVisitor v); 101 /** 102 * @param other The other layer that is tested to be mergable with this. 103 * @return Whether the other layer can be merged into this layer. 104 */ 105 abstract public boolean isMergable(Layer other); 108 106 109 abstract public Object getInfoComponent(); 110 111 abstract public Component[] getMenuEntries(); 112 113 /** 114 * Called, when the layer is removed from the mapview and is going to be 115 * destroyed. 116 * 117 * This is because the Layer constructor can not add itself safely as listener 118 * to the layerlist dialog, because there may be no such dialog yet (loaded 119 * via command line parameter). 120 */ 121 public void destroy() {} 107 abstract public void visitBoundingBox(BoundingXYVisitor v); 108 109 abstract public Object getInfoComponent(); 110 111 abstract public Component[] getMenuEntries(); 112 113 /** 114 * Called, when the layer is removed from the mapview and is going to be 115 * destroyed. 116 * 117 * This is because the Layer constructor can not add itself safely as listener 118 * to the layerlist dialog, because there may be no such dialog yet (loaded 119 * via command line parameter). 120 */ 121 public void destroy() {} 122 122 } -
trunk/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java
r627 r1169 8 8 public interface MapViewPaintable { 9 9 10 11 12 13 14 10 /** 11 * Paint the dataset using the engine set. 12 * @param mv The object that can translate GeoPoints to screen coordinates. 13 */ 14 abstract public void paint(Graphics g, MapView mv); 15 15 16 16 } -
trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
r1083 r1169 68 68 * A layer holding data from a specific dataset. 69 69 * The data can be fully edited. 70 * 70 * 71 71 * @author imi 72 72 */ … … 74 74 75 75 public final static class DataCountVisitor implements Visitor { 76 public final int[] normal = new int[3]; 76 public final int[] normal = new int[3]; 77 77 public final int[] deleted = new int[3]; 78 78 public final String[] names = {"node", "way", "relation"}; … … 131 131 */ 132 132 private static TexturePaint hatched; 133 133 134 134 static { 135 135 createHatchTexture(); … … 178 178 boolean inactive = !active && Main.pref.getBoolean("draw.data.inactive_color", true); 179 179 boolean virtual = !inactive && Main.map.mapView.useVirtualNodes(); 180 181 // draw the hatched area for non-downloaded region. only draw if we're the active 180 181 // draw the hatched area for non-downloaded region. only draw if we're the active 182 182 // and bounds are defined; don't draw for inactive layers or loaded GPX files etc 183 183 if (active && Main.pref.getBoolean("draw.data.downloaded_area", true) && !data.dataSources.isEmpty()) { 184 184 // initialize area with current viewport 185 185 Rectangle b = Main.map.mapView.getBounds(); 186 // on some platforms viewport bounds seem to be offset from the left, 186 // on some platforms viewport bounds seem to be offset from the left, 187 187 // over-grow it just to be sure 188 188 b.grow(100, 100); 189 189 Area a = new Area(b); 190 190 191 191 // now succesively subtract downloaded areas 192 192 for (DataSource src : data.dataSources) { … … 200 200 } 201 201 } 202 202 203 203 // paint remainder 204 204 ((Graphics2D)g).setPaint(hatched); 205 205 ((Graphics2D)g).fill(a); 206 206 } 207 207 208 208 SimplePaintVisitor painter; 209 209 if (Main.pref.getBoolean("draw.wireframe")) … … 234 234 235 235 // copy the merged layer's data source info 236 for (DataSource src : ((OsmDataLayer)from).data.dataSources) 236 for (DataSource src : ((OsmDataLayer)from).data.dataSources) 237 237 data.dataSources.add(src); 238 238 fireDataChange(); 239 239 // repaint to make sure new data is displayed properly. 240 240 Main.map.mapView.repaint(); 241 241 242 242 if (visitor.conflicts.isEmpty()) 243 243 return; … … 263 263 * really deleting all deleted objects and reset the modified flags. This is done 264 264 * after a successfull upload. 265 * 266 * @param processed A list of all objects that were actually uploaded. 267 * May be <code>null</code>, which means nothing has been uploaded but 265 * 266 * @param processed A list of all objects that were actually uploaded. 267 * May be <code>null</code>, which means nothing has been uploaded but 268 268 * saved to disk instead. Note that an empty collection for "processed" 269 269 * means that an upload has been attempted but failed. … … 274 274 if (processed != null && processed.isEmpty() && !dataAdded) 275 275 return; 276 276 277 277 Main.main.undoRedo.clean(); 278 278 … … 300 300 * Clean the modified flag for the given iterator over a collection if it is in the 301 301 * list of processed entries. 302 * 302 * 303 303 * @param it The iterator to change the modified and remove the items if deleted. 304 304 * @param processed A list of all objects that have been successfully progressed. … … 415 415 } 416 416 } 417 418 // what is this loop meant to do? it creates waypoints but never 417 418 // what is this loop meant to do? it creates waypoints but never 419 419 // records them? 420 420 for (Node n : data.nodes) { -
trunk/src/org/openstreetmap/josm/gui/layer/RawGpsLayer.java
r999 r1169 57 57 * A layer holding data from a gps source. 58 58 * The data is read only. 59 * 59 * 60 60 * @author imi 61 61 */ 62 62 public class RawGpsLayer extends Layer implements PreferenceChangedListener { 63 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 urlConn.setRequestProperty("Content-Type", 142 143 144 145 146 147 MultiPartFormOutputStream out = 148 149 150 151 152 153 154 155 // out.writeFile("myFile", "text/plain", "C:\\test.txt", 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 //return; // assume cancel188 189 190 191 } 192 193 194 195 196 197 198 199 200 201 latlon = ll; 202 eastNorth = Main.proj.latlon2eastNorth(ll); 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 64 public class ConvertToDataLayerAction extends AbstractAction { 65 public ConvertToDataLayerAction() { 66 super(tr("Convert to data layer"), ImageProvider.get("converttoosm")); 67 } 68 public void actionPerformed(ActionEvent e) { 69 JPanel msg = new JPanel(new GridBagLayout()); 70 msg.add(new JLabel(tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")), GBC.eol()); 71 msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop()); 72 if (!DontShowAgainInfo.show("convert_to_data", msg)) 73 return; 74 DataSet ds = new DataSet(); 75 for (Collection<GpsPoint> c : data) { 76 Way w = new Way(); 77 for (GpsPoint p : c) { 78 Node n = new Node(p.latlon); 79 ds.nodes.add(n); 80 w.nodes.add(n); 81 } 82 ds.ways.add(w); 83 } 84 Main.main.addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", RawGpsLayer.this.name), null)); 85 Main.main.removeLayer(RawGpsLayer.this); 86 } 87 } 88 89 public class UploadTraceAction extends AbstractAction { 90 public UploadTraceAction() { 91 super(tr("Upload this trace..."), ImageProvider.get("uploadtrace")); 92 } 93 public void actionPerformed(ActionEvent e) { 94 JPanel msg = new JPanel(new GridBagLayout()); 95 msg.add(new JLabel(tr("<html>This functionality has been added only recently. Please<br>"+ 96 "use with care and check if it works as expected.</html>")), GBC.eop()); 97 ButtonGroup bg = new ButtonGroup(); 98 JRadioButton c1 = null; 99 JRadioButton c2 = null; 100 101 if (associatedFile != null) { 102 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), false); 103 c2 = new JRadioButton(tr("Upload raw file: {0}", associatedFile.getName()), true); 104 } 105 else 106 { 107 c1 = new JRadioButton(tr("Upload track filtered by JOSM"), true); 108 c2 = new JRadioButton(tr("Upload raw file: "), false); 109 c2.setEnabled(false); 110 } 111 c1.setEnabled(false); 112 bg.add(c1); 113 bg.add(c2); 114 115 msg.add(c1, GBC.eol()); 116 msg.add(c2, GBC.eop()); 117 118 119 JTextField description = new JTextField(); 120 JTextField tags = new JTextField(); 121 msg.add(new JLabel(tr("Description:")), GBC.std()); 122 msg.add(description, GBC.eol().fill(GBC.HORIZONTAL)); 123 msg.add(new JLabel(tr("Tags:")), GBC.std()); 124 msg.add(tags, GBC.eol().fill(GBC.HORIZONTAL)); 125 JCheckBox c3 = new JCheckBox("public"); 126 msg.add(c3, GBC.eop()); 127 msg.add(new JLabel("Please ensure that you don't upload your traces twice."), GBC.eop()); 128 129 int answer = JOptionPane.showConfirmDialog(Main.parent, msg, tr("GPX-Upload"), JOptionPane.OK_CANCEL_OPTION); 130 if (answer == JOptionPane.OK_OPTION) 131 { 132 try { 133 String version = Main.pref.get("osm-server.version", "0.5"); 134 URL url = new URL(Main.pref.get("osm-server.url") + 135 "/" + version + "/gpx/create"); 136 137 // create a boundary string 138 String boundary = MultiPartFormOutputStream.createBoundary(); 139 URLConnection urlConn = MultiPartFormOutputStream.createConnection(url); 140 urlConn.setRequestProperty("Accept", "*/*"); 141 urlConn.setRequestProperty("Content-Type", 142 MultiPartFormOutputStream.getContentType(boundary)); 143 // set some other request headers... 144 urlConn.setRequestProperty("Connection", "Keep-Alive"); 145 urlConn.setRequestProperty("Cache-Control", "no-cache"); 146 // no need to connect cuz getOutputStream() does it 147 MultiPartFormOutputStream out = 148 new MultiPartFormOutputStream(urlConn.getOutputStream(), boundary); 149 out.writeField("description", description.getText()); 150 out.writeField("tags", tags.getText()); 151 out.writeField("public", (c3.getSelectedObjects() != null) ? "1" : "0"); 152 // upload a file 153 out.writeFile("gpx_file", "text/xml", associatedFile); 154 // can also write bytes directly 155 // out.writeFile("myFile", "text/plain", "C:\\test.txt", 156 // "This is some file text.".getBytes("ASCII")); 157 out.close(); 158 // read response from server 159 BufferedReader in = new BufferedReader( 160 new InputStreamReader(urlConn.getInputStream())); 161 String line = ""; 162 while((line = in.readLine()) != null) { 163 System.out.println(line); 164 } 165 in.close(); 166 167 /* 168 int retCode = activeConnection.getResponseCode(); 169 System.out.println("got return: "+retCode); 170 String retMsg = activeConnection.getResponseMessage(); 171 activeConnection.disconnect(); 172 if (retCode != 200) { 173 // Look for a detailed error message from the server 174 if (activeConnection.getHeaderField("Error") != null) 175 retMsg += "\n" + activeConnection.getHeaderField("Error"); 176 177 // Report our error 178 ByteArrayOutputStream o = new ByteArrayOutputStream(); 179 System.out.println(new String(o.toByteArray(), "UTF-8").toString()); 180 throw new RuntimeException(retCode+" "+retMsg); 181 } 182 */ 183 } catch (UnknownHostException ex) { 184 throw new RuntimeException(tr("Unknown host")+": "+ex.getMessage(), ex); 185 } catch (Exception ex) { 186 //if (cancel) 187 // return; // assume cancel 188 if (ex instanceof RuntimeException) 189 throw (RuntimeException)ex; 190 throw new RuntimeException(ex.getMessage(), ex); 191 } 192 } 193 } 194 } 195 196 public static class GpsPoint { 197 public final LatLon latlon; 198 public final EastNorth eastNorth; 199 public final String time; 200 public GpsPoint(LatLon ll, String t) { 201 latlon = ll; 202 eastNorth = Main.proj.latlon2eastNorth(ll); 203 time = t; 204 } 205 } 206 207 /** 208 * A list of ways which containing a list of points. 209 */ 210 public final Collection<Collection<GpsPoint>> data; 211 public final boolean fromServer; 212 213 public RawGpsLayer(boolean fromServer, Collection<Collection<GpsPoint>> data, String name, File associatedFile) { 214 super(name); 215 this.fromServer = fromServer; 216 this.associatedFile = associatedFile; 217 this.data = data; 218 Main.pref.listener.add(this); 219 } 220 221 /** 222 * Return a static icon. 223 */ 224 @Override public Icon getIcon() { 225 return ImageProvider.get("layer", "rawgps_small"); 226 } 227 228 @Override public void paint(Graphics g, MapView mv) { 229 g.setColor(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray)); 230 Point old = null; 231 232 boolean force = Main.pref.getBoolean("draw.rawgps.lines.force"); 233 boolean lines = Main.pref.getBoolean("draw.rawgps.lines"); 234 String linesKey = "draw.rawgps.lines.layer "+name; 235 if (Main.pref.hasKey(linesKey)) 236 lines = Main.pref.getBoolean(linesKey); 237 boolean large = Main.pref.getBoolean("draw.rawgps.large"); 238 for (Collection<GpsPoint> c : data) { 239 if (!force) 240 old = null; 241 for (GpsPoint p : c) { 242 Point screen = mv.getPoint(p.eastNorth); 243 if (lines && old != null) 244 g.drawLine(old.x, old.y, screen.x, screen.y); 245 else if (!large) 246 g.drawRect(screen.x, screen.y, 0, 0); 247 if (large) 248 g.fillRect(screen.x-1, screen.y-1, 3, 3); 249 old = screen; 250 } 251 } 252 } 253 254 @Override public String getToolTipText() { 255 int points = 0; 256 for (Collection<GpsPoint> c : data) 257 points += c.size(); 258 String tool = data.size()+" "+trn("track", "tracks", data.size()) 259 +" "+points+" "+trn("point", "points", points); 260 if (associatedFile != null) 261 tool = "<html>"+tool+"<br>"+associatedFile.getPath()+"</html>"; 262 return tool; 263 } 264 265 @Override public void mergeFrom(Layer from) { 266 RawGpsLayer layer = (RawGpsLayer)from; 267 data.addAll(layer.data); 268 } 269 270 @Override public boolean isMergable(Layer other) { 271 return other instanceof RawGpsLayer; 272 } 273 274 @Override public void visitBoundingBox(BoundingXYVisitor v) { 275 for (Collection<GpsPoint> c : data) 276 for (GpsPoint p : c) 277 v.visit(p.eastNorth); 278 } 279 280 @Override public Object getInfoComponent() { 281 StringBuilder b = new StringBuilder(); 282 int points = 0; 283 for (Collection<GpsPoint> c : data) { 284 b.append(" "+trn("a track with {0} point","a track with {0} points", c.size(), c.size())+"<br>"); 285 points += c.size(); 286 } 287 b.append("</html>"); 288 return "<html>"+trn("{0} consists of {1} track", "{0} consists of {1} tracks", data.size(), name, data.size())+" ("+trn("{0} point", "{0} points", points, points)+")<br>"+b.toString(); 289 } 290 291 @Override public Component[] getMenuEntries() { 292 JMenuItem line = new JMenuItem(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment")); 293 line.addActionListener(new ActionListener(){ 294 public void actionPerformed(ActionEvent e) { 295 JRadioButton[] r = new JRadioButton[3]; 296 r[0] = new JRadioButton(tr("Use global settings.")); 297 r[1] = new JRadioButton(tr("Draw lines between points for this layer.")); 298 r[2] = new JRadioButton(tr("Do not draw lines between points for this layer.")); 299 ButtonGroup group = new ButtonGroup(); 300 Box panel = Box.createVerticalBox(); 301 for (JRadioButton b : r) { 302 group.add(b); 303 panel.add(b); 304 } 305 String propName = "draw.rawgps.lines.layer "+name; 306 if (Main.pref.hasKey(propName)) 307 group.setSelected(r[Main.pref.getBoolean(propName) ? 1:2].getModel(), true); 308 else 309 group.setSelected(r[0].getModel(), true); 310 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION); 311 if (answer == JOptionPane.CANCEL_OPTION) 312 return; 313 if (group.getSelection() == r[0].getModel()) 314 Main.pref.put(propName, null); 315 else 316 Main.pref.put(propName, group.getSelection() == r[1].getModel()); 317 Main.map.repaint(); 318 } 319 }); 320 321 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser")); 322 color.addActionListener(new ActionListener(){ 323 public void actionPerformed(ActionEvent e) { 324 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps point"), "layer "+name, Color.gray)); 325 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 326 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, 327 JOptionPane.PLAIN_MESSAGE, null, options, options[0]); 328 switch (answer) { 329 case 0: 330 Main.pref.putColor("layer "+name, c.getColor()); 331 break; 332 case 1: 333 return; 334 case 2: 335 Main.pref.putColor("layer "+name, null); 336 break; 337 } 338 Main.map.repaint(); 339 } 340 }); 341 342 if (Main.applet) 343 return new Component[]{ 344 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 345 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 346 new JSeparator(), 347 color, 348 line, 349 new JMenuItem(new ConvertToDataLayerAction()), 350 //new JMenuItem(new UploadTraceAction()), 351 new JSeparator(), 352 new JMenuItem(new RenameLayerAction(associatedFile, this)), 353 new JSeparator(), 354 new JMenuItem(new LayerListPopup.InfoAction(this))}; 355 return new Component[]{ 356 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)), 357 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), 358 new JSeparator(), 359 new JMenuItem(new GpxExportAction(this)), 360 color, 361 line, 362 new JMenuItem(new ConvertToDataLayerAction()), 363 //new JMenuItem(new UploadTraceAction()), 364 new JSeparator(), 365 new JMenuItem(new RenameLayerAction(associatedFile, this)), 366 new JSeparator(), 367 new JMenuItem(new LayerListPopup.InfoAction(this))}; 368 } 369 370 public void preferenceChanged(String key, String newValue) { 371 if (Main.map != null && (key.equals("draw.rawgps.lines") || key.equals("draw.rawgps.lines.force"))) 372 Main.map.repaint(); 373 } 374 375 @Override public void destroy() { 376 Main.pref.listener.remove(RawGpsLayer.this); 377 377 } 378 378 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarker.java
r627 r1169 11 11 /** 12 12 * Marker class with audio playback capability. 13 * 13 * 14 14 * @author Frederik Ramm <frederik@remote.org> 15 15 * … … 17 17 public class AudioMarker extends ButtonMarker { 18 18 19 private URL audioUrl; 20 private static AudioMarker recentlyPlayedMarker = null; 21 public double syncOffset; 22 23 /** 24 * Verifies the parameter whether a new AudioMarker can be created and return 25 * one or return <code>null</code>. 26 */ 27 public static AudioMarker create(LatLon ll, String text, String url, MarkerLayer parentLayer, double time, double offset) { 28 try { 29 return new AudioMarker(ll, text, new URL(url), parentLayer, time, offset); 30 } catch (Exception ex) { 31 return null; 32 } 33 } 19 private URL audioUrl; 20 private static AudioMarker recentlyPlayedMarker = null; 21 public double syncOffset; 34 22 35 private AudioMarker(LatLon ll, String text, URL audioUrl, MarkerLayer parentLayer, double time, double offset) { 36 super(ll, text, "speech.png", parentLayer, time, offset); 37 this.audioUrl = audioUrl; 38 this.syncOffset = 0.0; 39 } 23 /** 24 * Verifies the parameter whether a new AudioMarker can be created and return 25 * one or return <code>null</code>. 26 */ 27 public static AudioMarker create(LatLon ll, String text, String url, MarkerLayer parentLayer, double time, double offset) { 28 try { 29 return new AudioMarker(ll, text, new URL(url), parentLayer, time, offset); 30 } catch (Exception ex) { 31 return null; 32 } 33 } 40 34 41 @Override public void actionPerformed(ActionEvent ev) { 42 play(); 43 } 35 private AudioMarker(LatLon ll, String text, URL audioUrl, MarkerLayer parentLayer, double time, double offset) { 36 super(ll, text, "speech.png", parentLayer, time, offset); 37 this.audioUrl = audioUrl; 38 this.syncOffset = 0.0; 39 } 44 40 45 public static AudioMarker recentlyPlayedMarker() { 46 return recentlyPlayedMarker; 47 } 48 49 public URL url() { 50 return audioUrl; 51 } 52 53 /** 54 * Starts playing the audio associated with the marker offset by the given amount 55 * @param after : seconds after marker where playing should start 56 */ 57 public void play(double after) { 58 try { 59 // first enable tracing the audio along the track 60 Main.map.mapView.playHeadMarker.animate(); 41 @Override public void actionPerformed(ActionEvent ev) { 42 play(); 43 } 61 44 62 AudioPlayer.play(audioUrl, offset + syncOffset + after); 63 recentlyPlayedMarker = this; 64 } catch (Exception e) { 65 AudioPlayer.audioMalfunction(e); 66 } 67 } 45 public static AudioMarker recentlyPlayedMarker() { 46 return recentlyPlayedMarker; 47 } 68 48 69 /** 70 * Starts playing the audio associated with the marker: used in response to pressing 71 * the marker as well as indirectly 72 * 73 */ 74 public void play() { play(0.0); } 49 public URL url() { 50 return audioUrl; 51 } 75 52 76 public void adjustOffset(double adjustment) { 77 syncOffset = adjustment; // added to offset may turn out negative, but that's ok 78 } 53 /** 54 * Starts playing the audio associated with the marker offset by the given amount 55 * @param after : seconds after marker where playing should start 56 */ 57 public void play(double after) { 58 try { 59 // first enable tracing the audio along the track 60 Main.map.mapView.playHeadMarker.animate(); 79 61 80 public double syncOffset() { 81 return syncOffset; 82 } 83 84 public static String inventName (double offset) { 85 int wholeSeconds = (int)(offset + 0.5); 86 if (wholeSeconds < 60) 87 return Integer.toString(wholeSeconds); 88 else if (wholeSeconds < 3600) 89 return String.format("%d:%02d", wholeSeconds / 60, wholeSeconds % 60); 90 else 91 return String.format("%d:%02d:%02d", wholeSeconds / 3600, (wholeSeconds % 3600)/60, wholeSeconds % 60); 92 } 62 AudioPlayer.play(audioUrl, offset + syncOffset + after); 63 recentlyPlayedMarker = this; 64 } catch (Exception e) { 65 AudioPlayer.audioMalfunction(e); 66 } 67 } 68 69 /** 70 * Starts playing the audio associated with the marker: used in response to pressing 71 * the marker as well as indirectly 72 * 73 */ 74 public void play() { play(0.0); } 75 76 public void adjustOffset(double adjustment) { 77 syncOffset = adjustment; // added to offset may turn out negative, but that's ok 78 } 79 80 public double syncOffset() { 81 return syncOffset; 82 } 83 84 public static String inventName (double offset) { 85 int wholeSeconds = (int)(offset + 0.5); 86 if (wholeSeconds < 60) 87 return Integer.toString(wholeSeconds); 88 else if (wholeSeconds < 3600) 89 return String.format("%d:%02d", wholeSeconds / 60, wholeSeconds % 60); 90 else 91 return String.format("%d:%02d:%02d", wholeSeconds / 3600, (wholeSeconds % 3600)/60, wholeSeconds % 60); 92 } 93 93 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/ButtonMarker.java
r627 r1169 17 17 /** 18 18 * Marker class with button look-and-feel. 19 * 19 * 20 20 * @author Frederik Ramm <frederik@remote.org> 21 21 * … … 23 23 public class ButtonMarker extends Marker { 24 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 25 private Rectangle buttonRectangle; 26 27 public ButtonMarker(LatLon ll, String buttonImage, MarkerLayer parentLayer, double time, double offset) { 28 super(ll, null, buttonImage, parentLayer, time, offset); 29 buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight()); 30 } 31 32 public ButtonMarker(LatLon ll, String text, String buttonImage, MarkerLayer parentLayer, double time, double offset) { 33 super(ll, text, buttonImage, parentLayer, time, offset); 34 buttonRectangle = new Rectangle(0, 0, symbol.getIconWidth(), symbol.getIconHeight()); 35 } 36 37 @Override public boolean containsPoint(Point p) { 38 Point screen = Main.map.mapView.getPoint(eastNorth); 39 buttonRectangle.setLocation(screen.x+4, screen.y+2); 40 return buttonRectangle.contains(p); 41 } 42 43 @Override public void paint(Graphics g, MapView mv, boolean mousePressed, String show) { 44 if (! show.equalsIgnoreCase("show")) { 45 super.paint(g, mv, mousePressed, show); 46 return; 47 } 48 Point screen = mv.getPoint(eastNorth); 49 buttonRectangle.setLocation(screen.x+4, screen.y+2); 50 symbol.paintIcon(mv, g, screen.x+4, screen.y+2); 51 Border b; 52 Point mousePosition = mv.getMousePosition(); 53 54 if (mousePosition != null) { 55 // mouse is inside the window 56 if (mousePressed) { 57 b = BorderFactory.createBevelBorder(BevelBorder.LOWERED); 58 } else { 59 b = BorderFactory.createBevelBorder(BevelBorder.RAISED); 60 } 61 Insets inset = b.getBorderInsets(mv); 62 Rectangle r = new Rectangle(buttonRectangle); 63 r.grow((inset.top+inset.bottom)/2, (inset.left+inset.right)/2); 64 b.paintBorder(mv, g, r.x, r.y, r.width, r.height); 65 } 66 if ((text != null) && (show.equalsIgnoreCase("show")) && Main.pref.getBoolean("marker.buttonlabels", true)) 67 g.drawString(text, screen.x+4, screen.y+2); 68 } 69 69 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java
r627 r1169 26 26 * Marker representing an image. Uses a special icon, and when clicked, 27 27 * displays an image view dialog. Re-uses some code from GeoImageLayer. 28 * 28 * 29 29 * @author Frederik Ramm <frederik@remote.org> 30 30 * … … 32 32 public class ImageMarker extends ButtonMarker { 33 33 34 34 public URL imageUrl; 35 35 36 37 38 39 40 41 42 36 public static ImageMarker create(LatLon ll, String url, MarkerLayer parentLayer, double time, double offset) { 37 try { 38 return new ImageMarker(ll, new URL(url), parentLayer, time, offset); 39 } catch (Exception ex) { 40 return null; 41 } 42 } 43 43 44 45 46 47 44 private ImageMarker(LatLon ll, URL imageUrl, MarkerLayer parentLayer, double time, double offset) { 45 super(ll, "photo.png", parentLayer, time, offset); 46 this.imageUrl = imageUrl; 47 } 48 48 49 50 51 52 53 49 @Override public void actionPerformed(ActionEvent ev) { 50 final JPanel p = new JPanel(new BorderLayout()); 51 final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(imageUrl, 580))); 52 final JViewport vp = scroll.getViewport(); 53 p.add(scroll, BorderLayout.CENTER); 54 54 55 55 final JToggleButton scale = new JToggleButton(ImageProvider.get("misc", "rectangle")); 56 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 57 JPanel p2 = new JPanel(); 58 p2.add(scale); 59 p.add(p2, BorderLayout.SOUTH); 60 scale.addActionListener(new ActionListener(){ 61 public void actionPerformed(ActionEvent ev) { 62 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 63 if (scale.getModel().isSelected()) 64 ((JLabel)vp.getView()).setIcon(loadScaledImage(imageUrl, Math.max(vp.getWidth(), vp.getHeight()))); 65 else 66 ((JLabel)vp.getView()).setIcon(new ImageIcon(imageUrl)); 67 p.setCursor(Cursor.getDefaultCursor()); 68 } 69 }); 70 scale.setSelected(true); 71 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE); 72 JDialog dlg = pane.createDialog(Main.parent, imageUrl.toString()); 73 dlg.setModal(false); 74 dlg.setVisible(true); 75 } 76 76 77 78 79 80 81 82 83 84 85 86 87 88 89 77 private static Icon loadScaledImage(URL u, int maxSize) { 78 Image img = new ImageIcon(u).getImage(); 79 int w = img.getWidth(null); 80 int h = img.getHeight(null); 81 if (w>h) { 82 h = Math.round(maxSize*((float)h/w)); 83 w = maxSize; 84 } else { 85 w = Math.round(maxSize*((float)w/h)); 86 h = maxSize; 87 } 88 return new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH)); 89 } 90 90 91 91 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/Marker.java
r655 r1169 23 23 24 24 /** 25 * Basic marker class. Requires a position, and supports 25 * Basic marker class. Requires a position, and supports 26 26 * a custom icon and a name. 27 * 27 * 28 28 * This class is also used to create appropriate Marker-type objects 29 29 * when waypoints are imported. 30 * 30 * 31 31 * It hosts a public list object, named makers, containing implementations of 32 * the MarkerMaker interface. Whenever a Marker needs to be created, each 32 * the MarkerMaker interface. Whenever a Marker needs to be created, each 33 33 * object in makers is called with the waypoint parameters (Lat/Lon and tag 34 34 * data), and the first one to return a Marker object wins. 35 * 35 * 36 36 * By default, one the list contains one default "Maker" implementation that 37 * will create AudioMarkers for .wav files, ImageMarkers for .png/.jpg/.jpeg 37 * will create AudioMarkers for .wav files, ImageMarkers for .png/.jpg/.jpeg 38 38 * files, and WebMarkers for everything else. (The creation of a WebMarker will 39 39 * fail if there's no vaild URL in the <link> tag, so it might still make sense 40 40 * to add Makers for such waypoints at the end of the list.) 41 * 41 * 42 42 * The default implementation only looks at the value of the <link> tag inside 43 43 * the <wpt> tag of the GPX file. 44 * 44 * 45 45 * <h2>HowTo implement a new Marker</h2> 46 46 * <ul> … … 54 54 * if you only add a new marker style.</li> 55 55 * </ul> 56 * 56 * 57 57 * @author Frederik Ramm <frederik@remote.org> 58 58 */ 59 59 public class Marker implements ActionListener { 60 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 61 public EastNorth eastNorth; 62 public final String text; 63 public final Icon symbol; 64 public final MarkerLayer parentLayer; 65 public double time; /* avbsolute time of marker since epocj */ 66 public double offset; /* time offset in seconds from the gpx point from which it was derived, 67 may be adjusted later to sync with other data, so not final */ 68 69 /** 70 * Plugins can add their Marker creation stuff at the bottom or top of this list 71 * (depending on whether they want to override default behaviour or just add new 72 * stuff). 73 */ 74 public static LinkedList<MarkerProducers> markerProducers = new LinkedList<MarkerProducers>(); 75 76 // Add one Maker specifying the default behaviour. 77 static { 78 Marker.markerProducers.add(new MarkerProducers() { 79 public Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) { 80 String uri = null; 81 // cheapest way to check whether "link" object exists and is a non-empty 82 // collection of GpxLink objects... 83 try { 84 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) { 85 uri = oneLink.uri; 86 break; 87 } 88 } catch (Exception ex) {} 89 90 // Try a relative file:// url, if the link is not in an URL-compatible form 91 91 if (relativePath != null && uri != null && !isWellFormedAddress(uri)) 92 92 uri = new File(relativePath.getParentFile(), uri).toURI().toString(); … … 94 94 String name_desc = ""; 95 95 if (wpt.attr.containsKey("name")) { 96 96 name_desc = wpt.getString("name"); 97 97 } else if (wpt.attr.containsKey("desc")) { 98 98 name_desc = wpt.getString("desc"); 99 99 } 100 100 101 101 if (uri == null) 102 102 return new Marker(wpt.latlon, name_desc, wpt.getString("symbol"), parentLayer, time, offset); … … 104 104 return AudioMarker.create(wpt.latlon, name_desc, uri, parentLayer, time, offset); 105 105 else if (uri.endsWith(".png") || uri.endsWith(".jpg") || uri.endsWith(".jpeg") || uri.endsWith(".gif")) 106 return ImageMarker.create(wpt.latlon, uri, parentLayer, time, offset); 107 else 108 return WebMarker.create(wpt.latlon, uri, parentLayer, time, offset); 109 } 110 111 private boolean isWellFormedAddress(String link) { 112 try { 113 new URL(link); 114 return true; 115 } catch (MalformedURLException x) { 116 return false; 117 } 106 return ImageMarker.create(wpt.latlon, uri, parentLayer, time, offset); 107 else 108 return WebMarker.create(wpt.latlon, uri, parentLayer, time, offset); 118 109 } 119 }); 120 } 121 122 public Marker(LatLon ll, String text, String iconName, MarkerLayer parentLayer, double time, double offset) { 123 eastNorth = Main.proj.latlon2eastNorth(ll); 124 this.text = text; 125 this.offset = offset; 126 this.time = time; 127 Icon symbol = ImageProvider.getIfAvailable("markers",iconName); 128 if (symbol == null) 129 symbol = ImageProvider.getIfAvailable("symbols",iconName); 130 if (symbol == null) 131 symbol = ImageProvider.getIfAvailable("nodes",iconName); 132 this.symbol = symbol; 133 this.parentLayer = parentLayer; 134 } 135 136 /** 137 * Checks whether the marker display area contains the given point. 138 * Markers not interested in mouse clicks may always return false. 139 * 140 * @param p The point to check 141 * @return <code>true</code> if the marker "hotspot" contains the point. 142 */ 143 public boolean containsPoint(Point p) { 144 return false; 145 } 146 147 /** 148 * Called when the mouse is clicked in the marker's hotspot. Never 149 * called for markers which always return false from containsPoint. 150 * 151 * @param ev A dummy ActionEvent 152 */ 153 public void actionPerformed(ActionEvent ev) { 154 } 155 156 /** 157 * Paints the marker. 158 * @param g graphics context 159 * @param mv map view 160 * @param mousePressed true if the left mouse button is pressed 161 */ 162 public void paint(Graphics g, MapView mv, boolean mousePressed, String show) { 163 Point screen = mv.getPoint(eastNorth); 164 if (symbol != null && show.equalsIgnoreCase("show")) { 165 symbol.paintIcon(mv, g, screen.x-symbol.getIconWidth()/2, screen.y-symbol.getIconHeight()/2); 166 } else { 167 g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2); 168 g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2); 169 } 170 171 if ((text != null) && (show.equalsIgnoreCase("show"))) 172 g.drawString(text, screen.x+4, screen.y+2); 173 } 174 175 /** 176 * Returns an object of class Marker or one of its subclasses 177 * created from the parameters given. 178 * 179 * @param wpt waypoint data for marker 180 * @param relativePath An path to use for constructing relative URLs or 181 * <code>null</code> for no relative URLs 182 * @param offset double in seconds as the time offset of this marker from 183 * the GPX file from which it was derived (if any). 184 * @return a new Marker object 185 */ 186 public static Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) { 187 for (MarkerProducers maker : Marker.markerProducers) { 188 Marker marker = maker.createMarker(wpt, relativePath, parentLayer, time, offset); 189 if (marker != null) 190 return marker; 191 } 192 return null; 193 } 194 195 /** 196 * Returns an AudioMarker derived from this Marker and the provided uri 197 * Subclasses of specific marker types override this to return null as they can't 198 * be turned into AudioMarkers. This includes AudioMarkers themselves, as they 199 * already have audio. 200 * 201 * @param uri uri of wave file 202 * @return AudioMarker 203 */ 204 205 public AudioMarker audioMarkerFromMarker(String uri) { 206 AudioMarker audioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(this.eastNorth), this.text, uri, this.parentLayer, this.time, this.offset); 207 return audioMarker; 208 } 110 111 private boolean isWellFormedAddress(String link) { 112 try { 113 new URL(link); 114 return true; 115 } catch (MalformedURLException x) { 116 return false; 117 } 118 } 119 }); 120 } 121 122 public Marker(LatLon ll, String text, String iconName, MarkerLayer parentLayer, double time, double offset) { 123 eastNorth = Main.proj.latlon2eastNorth(ll); 124 this.text = text; 125 this.offset = offset; 126 this.time = time; 127 Icon symbol = ImageProvider.getIfAvailable("markers",iconName); 128 if (symbol == null) 129 symbol = ImageProvider.getIfAvailable("symbols",iconName); 130 if (symbol == null) 131 symbol = ImageProvider.getIfAvailable("nodes",iconName); 132 this.symbol = symbol; 133 this.parentLayer = parentLayer; 134 } 135 136 /** 137 * Checks whether the marker display area contains the given point. 138 * Markers not interested in mouse clicks may always return false. 139 * 140 * @param p The point to check 141 * @return <code>true</code> if the marker "hotspot" contains the point. 142 */ 143 public boolean containsPoint(Point p) { 144 return false; 145 } 146 147 /** 148 * Called when the mouse is clicked in the marker's hotspot. Never 149 * called for markers which always return false from containsPoint. 150 * 151 * @param ev A dummy ActionEvent 152 */ 153 public void actionPerformed(ActionEvent ev) { 154 } 155 156 /** 157 * Paints the marker. 158 * @param g graphics context 159 * @param mv map view 160 * @param mousePressed true if the left mouse button is pressed 161 */ 162 public void paint(Graphics g, MapView mv, boolean mousePressed, String show) { 163 Point screen = mv.getPoint(eastNorth); 164 if (symbol != null && show.equalsIgnoreCase("show")) { 165 symbol.paintIcon(mv, g, screen.x-symbol.getIconWidth()/2, screen.y-symbol.getIconHeight()/2); 166 } else { 167 g.drawLine(screen.x-2, screen.y-2, screen.x+2, screen.y+2); 168 g.drawLine(screen.x+2, screen.y-2, screen.x-2, screen.y+2); 169 } 170 171 if ((text != null) && (show.equalsIgnoreCase("show"))) 172 g.drawString(text, screen.x+4, screen.y+2); 173 } 174 175 /** 176 * Returns an object of class Marker or one of its subclasses 177 * created from the parameters given. 178 * 179 * @param wpt waypoint data for marker 180 * @param relativePath An path to use for constructing relative URLs or 181 * <code>null</code> for no relative URLs 182 * @param offset double in seconds as the time offset of this marker from 183 * the GPX file from which it was derived (if any). 184 * @return a new Marker object 185 */ 186 public static Marker createMarker(WayPoint wpt, File relativePath, MarkerLayer parentLayer, double time, double offset) { 187 for (MarkerProducers maker : Marker.markerProducers) { 188 Marker marker = maker.createMarker(wpt, relativePath, parentLayer, time, offset); 189 if (marker != null) 190 return marker; 191 } 192 return null; 193 } 194 195 /** 196 * Returns an AudioMarker derived from this Marker and the provided uri 197 * Subclasses of specific marker types override this to return null as they can't 198 * be turned into AudioMarkers. This includes AudioMarkers themselves, as they 199 * already have audio. 200 * 201 * @param uri uri of wave file 202 * @return AudioMarker 203 */ 204 205 public AudioMarker audioMarkerFromMarker(String uri) { 206 AudioMarker audioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(this.eastNorth), this.text, uri, this.parentLayer, this.time, this.offset); 207 return audioMarker; 208 } 209 209 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
r999 r1169 43 43 /** 44 44 * A layer holding markers. 45 * 45 * 46 46 * Markers are GPS points with a name and, optionally, a symbol code attached; 47 47 * marker layers can be created from waypoints when importing raw GPS data, 48 48 * but they may also come from other sources. 49 * 49 * 50 50 * The symbol code is for future use. 51 * 51 * 52 52 * The data is read only. 53 53 */ 54 54 public class MarkerLayer extends Layer { 55 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 56 /** 57 * A list of markers. 58 */ 59 public final Collection<Marker> data; 60 private boolean mousePressed = false; 61 public GpxLayer fromLayer = null; 62 63 /* 64 private Icon audioTracerIcon = null; 65 private EastNorth playheadPosition = null; 66 private static Timer timer = null; 67 private static double audioAnimationInterval = 0.0; // seconds 68 private static double playheadTime = -1.0; 69 */ 70 public MarkerLayer(GpxData indata, String name, File associatedFile, GpxLayer fromLayer) { 71 72 super(name); 73 this.associatedFile = associatedFile; 74 this.data = new ArrayList<Marker>(); 75 this.fromLayer = fromLayer; 76 double firstTime = -1.0; 77 String lastLinkedFile = ""; 78 79 for (WayPoint wpt : indata.waypoints) { 80 /* calculate time differences in waypoints */ 81 double time = wpt.time; 82 boolean wpt_has_link = wpt.attr.containsKey("link"); 83 if (firstTime < 0 && wpt_has_link) { 84 firstTime = time; 85 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) { 86 lastLinkedFile = oneLink.uri; 87 break; 88 } 89 } 90 if (wpt_has_link) { 91 for (GpxLink oneLink : (Collection<GpxLink>) wpt.attr.get("link")) { 92 if (!oneLink.uri.equals(lastLinkedFile))firstTime = time; 93 lastLinkedFile = oneLink.uri; 94 break; 95 } 96 } 97 97 Marker m = Marker.createMarker(wpt, indata.storageFile, this, time, time - firstTime); 98 98 if (m != null) 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 } 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 AudioMarker am = null; 297 298 299 300 301 302 303 304 305 306 307 308 309 310 AudioMarker newAudioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(en), 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 99 data.add(m); 100 } 101 102 SwingUtilities.invokeLater(new Runnable(){ 103 public void run() { 104 Main.map.mapView.addMouseListener(new MouseAdapter() { 105 @Override public void mousePressed(MouseEvent e) { 106 if (e.getButton() != MouseEvent.BUTTON1) 107 return; 108 boolean mousePressedInButton = false; 109 if (e.getPoint() != null) { 110 for (Marker mkr : data) { 111 if (mkr.containsPoint(e.getPoint())) { 112 mousePressedInButton = true; 113 break; 114 } 115 } 116 } 117 if (! mousePressedInButton) 118 return; 119 mousePressed = true; 120 if (visible) 121 Main.map.mapView.repaint(); 122 } 123 @Override public void mouseReleased(MouseEvent ev) { 124 if (ev.getButton() != MouseEvent.BUTTON1 || ! mousePressed) 125 return; 126 mousePressed = false; 127 if (!visible) 128 return; 129 if (ev.getPoint() != null) { 130 for (Marker mkr : data) { 131 if (mkr.containsPoint(ev.getPoint())) 132 mkr.actionPerformed(new ActionEvent(this, 0, null)); 133 } 134 } 135 Main.map.mapView.repaint(); 136 } 137 }); 138 } 139 }); 140 } 141 142 /** 143 * Return a static icon. 144 */ 145 @Override public Icon getIcon() { 146 return ImageProvider.get("layer", "marker_small"); 147 } 148 149 @Override public void paint(Graphics g, MapView mv) { 150 boolean mousePressedTmp = mousePressed; 151 Point mousePos = mv.getMousePosition(); 152 String mkrTextShow = Main.pref.get("marker.show "+name, "show"); 153 154 g.setColor(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray)); 155 156 for (Marker mkr : data) { 157 if (mousePos != null && mkr.containsPoint(mousePos)) { 158 mkr.paint(g, mv, mousePressedTmp, mkrTextShow); 159 mousePressedTmp = false; 160 } else { 161 mkr.paint(g, mv, false, mkrTextShow); 162 } 163 } 164 } 165 166 @Override public String getToolTipText() { 167 return data.size()+" "+trn("marker", "markers", data.size()); 168 } 169 170 @Override public void mergeFrom(Layer from) { 171 MarkerLayer layer = (MarkerLayer)from; 172 data.addAll(layer.data); 173 } 174 175 @Override public boolean isMergable(Layer other) { 176 return other instanceof MarkerLayer; 177 } 178 179 @Override public void visitBoundingBox(BoundingXYVisitor v) { 180 for (Marker mkr : data) 181 v.visit(mkr.eastNorth); 182 } 183 184 @Override public Object getInfoComponent() { 185 return "<html>"+trn("{0} consists of {1} marker", "{0} consists of {1} markers", data.size(), name, data.size()) + "</html>"; 186 } 187 188 @Override public Component[] getMenuEntries() { 189 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser")); 190 color.putClientProperty("help", "Action/LayerCustomizeColor"); 191 color.addActionListener(new ActionListener(){ 192 public void actionPerformed(ActionEvent e) { 193 JColorChooser c = new JColorChooser(Main.pref.getColor(marktr("gps marker"), "layer "+name, Color.gray)); 194 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 195 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, 196 JOptionPane.PLAIN_MESSAGE, null, options, options[0]); 197 switch (answer) { 198 case 0: 199 Main.pref.putColor("layer "+name, c.getColor()); 200 break; 201 case 1: 202 return; 203 case 2: 204 Main.pref.putColor("layer "+name, null); 205 break; 206 } 207 Main.map.repaint(); 208 } 209 }); 210 211 JMenuItem syncaudio = new JMenuItem(tr("Synchronize Audio"), ImageProvider.get("audio-sync")); 212 syncaudio.putClientProperty("help", "Action/SynchronizeAudio"); 213 syncaudio.addActionListener(new ActionListener(){ 214 public void actionPerformed(ActionEvent e) { 215 if (! AudioPlayer.paused()) { 216 JOptionPane.showMessageDialog(Main.parent,tr("You need to pause audio at the moment when you hear your synchronization cue.")); 217 return; 218 } 219 AudioMarker recent = AudioMarker.recentlyPlayedMarker(); 220 if (synchronizeAudioMarkers(recent)) { 221 JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", recent.text)); 222 } else { 223 JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played.")); 224 } 225 } 226 }); 227 228 JMenuItem moveaudio = new JMenuItem(tr("Make Audio Marker At Play Head"), ImageProvider.get("addmarkers")); 229 moveaudio.putClientProperty("help", "Action/MakeAudioMarkerAtPlayHead"); 230 moveaudio.addActionListener(new ActionListener(){ 231 public void actionPerformed(ActionEvent e) { 232 if (! AudioPlayer.paused()) { 233 JOptionPane.showMessageDialog(Main.parent,tr("You need to have paused audio at the point on the track where you want the marker.")); 234 return; 235 } 236 PlayHeadMarker playHeadMarker = Main.map.mapView.playHeadMarker; 237 if (playHeadMarker == null) 238 return; 239 addAudioMarker(playHeadMarker.time, playHeadMarker.eastNorth); 240 Main.map.mapView.repaint(); 241 } 242 }); 243 244 Collection<Component> components = new ArrayList<Component>(); 245 components.add(new JMenuItem(new LayerListDialog.ShowHideLayerAction(this))); 246 components.add(new JMenuItem(new LayerListDialog.ShowHideMarkerText(this))); 247 components.add(new JMenuItem(new LayerListDialog.DeleteLayerAction(this))); 248 components.add(new JSeparator()); 249 components.add(color); 250 components.add(new JSeparator()); 251 components.add(syncaudio); 252 if (Main.pref.getBoolean("marker.traceaudio", true)) { 253 components.add (moveaudio); 254 } 255 components.add(new JMenuItem(new RenameLayerAction(associatedFile, this))); 256 components.add(new JSeparator()); 257 components.add(new JMenuItem(new LayerListPopup.InfoAction(this))); 258 return components.toArray(new Component[0]); 259 } 260 261 public boolean synchronizeAudioMarkers(AudioMarker startMarker) { 262 if (startMarker != null && ! data.contains(startMarker)) { 263 startMarker = null; 264 } 265 if (startMarker == null) { 266 // find the first audioMarker in this layer 267 for (Marker m : data) { 268 if (m instanceof AudioMarker) { 269 startMarker = (AudioMarker) m; 270 break; 271 } 272 } 273 } 274 if (startMarker == null) 275 return false; 276 277 // apply adjustment to all subsequent audio markers in the layer 278 double adjustment = AudioPlayer.position() - startMarker.offset; // in seconds 279 boolean seenStart = false; 280 URL url = startMarker.url(); 281 for (Marker m : data) { 282 if (m == startMarker) 283 seenStart = true; 284 if (seenStart) { 285 AudioMarker ma = (AudioMarker) m; // it must be an AudioMarker 286 if (ma.url().equals(url)) 287 ma.adjustOffset(adjustment); 288 } 289 } 290 return true; 291 } 292 293 public AudioMarker addAudioMarker(double time, EastNorth en) { 294 // find first audio marker to get absolute start time 295 double offset = 0.0; 296 AudioMarker am = null; 297 for (Marker m : data) { 298 if (m.getClass() == AudioMarker.class) { 299 am = (AudioMarker)m; 300 offset = time - am.time; 301 break; 302 } 303 } 304 if (am == null) { 305 JOptionPane.showMessageDialog(Main.parent,tr("No existing audio markers in this layer to offset from.")); 306 return null; 307 } 308 309 // make our new marker 310 AudioMarker newAudioMarker = AudioMarker.create(Main.proj.eastNorth2latlon(en), 311 AudioMarker.inventName(offset), AudioPlayer.url().toString(), this, time, offset); 312 313 // insert it at the right place in a copy the collection 314 Collection<Marker> newData = new ArrayList<Marker>(); 315 am = null; 316 AudioMarker ret = newAudioMarker; // save to have return value 317 for (Marker m : data) { 318 if (m.getClass() == AudioMarker.class) { 319 am = (AudioMarker) m; 320 if (newAudioMarker != null && offset < am.offset) { 321 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor 322 newData.add(newAudioMarker); 323 newAudioMarker = null; 324 } 325 } 326 newData.add(m); 327 } 328 329 if (newAudioMarker != null) { 330 if (am != null) 331 newAudioMarker.adjustOffset(am.syncOffset()); // i.e. same as predecessor 332 newData.add(newAudioMarker); // insert at end 333 } 334 335 // replace the collection 336 data.clear(); 337 data.addAll(newData); 338 return ret; 339 } 340 341 public static void playAudio() { 342 if (Main.map == null || Main.map.mapView == null) 343 return; 344 for (Layer layer : Main.map.mapView.getAllLayers()) { 345 if (layer.getClass() == MarkerLayer.class) { 346 MarkerLayer markerLayer = (MarkerLayer) layer; 347 for (Marker marker : markerLayer.data) { 348 if (marker.getClass() == AudioMarker.class) { 349 ((AudioMarker)marker).play(); 350 break; 351 } 352 } 353 } 354 } 355 } 356 357 public static void playNextMarker() { 358 playAdjacentMarker(true); 359 } 360 361 public static void playPreviousMarker() { 362 playAdjacentMarker(false); 363 } 364 365 private static void playAdjacentMarker(boolean next) { 366 Marker startMarker = AudioMarker.recentlyPlayedMarker(); 367 if (startMarker == null) { 368 // message? 369 return; 370 } 371 Marker previousMarker = null; 372 boolean nextTime = false; 373 if (Main.map == null || Main.map.mapView == null) 374 return; 375 for (Layer layer : Main.map.mapView.getAllLayers()) { 376 if (layer.getClass() == MarkerLayer.class) { 377 MarkerLayer markerLayer = (MarkerLayer) layer; 378 for (Marker marker : markerLayer.data) { 379 if (marker == startMarker) { 380 if (next) { 381 nextTime = true; 382 } else { 383 if (previousMarker == null) 384 previousMarker = startMarker; // if no previous one, play the first one again 385 ((AudioMarker)previousMarker).play(); 386 break; 387 } 388 } else if (nextTime && marker.getClass() == AudioMarker.class) { 389 ((AudioMarker)marker).play(); 390 return; 391 } 392 if (marker.getClass() == AudioMarker.class) 393 previousMarker = marker; 394 } 395 if (nextTime) { 396 // there was no next marker in that layer, so play the last one again 397 ((AudioMarker)startMarker).play(); 398 return; 399 } 400 } 401 } 402 } 403 404 404 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerProducers.java
r655 r1169 8 8 /** 9 9 * This interface has to be implemented by anyone who wants to create markers. 10 * 11 * When reading a gpx file, all implementations of MarkerMaker registered with 10 * 11 * When reading a gpx file, all implementations of MarkerMaker registered with 12 12 * the Marker are consecutively called until one returns a Marker object. 13 * 13 * 14 14 * @author Frederik Ramm <frederik@remote.org> 15 15 */ 16 16 public interface MarkerProducers { 17 18 19 20 * 21 * @param wp waypoint data 22 * @param relativePath An path to use for constructing relative URLs or 23 24 25 26 17 /** 18 * Returns a Marker object if this implementation wants to create one for the 19 * given input data, or <code>null</code> otherwise. 20 * 21 * @param wp waypoint data 22 * @param relativePath An path to use for constructing relative URLs or 23 * <code>null</code> for no relative URLs 24 * @return A Marker object, or <code>null</code>. 25 */ 26 public Marker createMarker(WayPoint wp, File relativePath, MarkerLayer parentLayer, double time, double offset); 27 27 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
r1026 r1169 31 31 /** 32 32 * Singleton marker class to track position of audio. 33 * 33 * 34 34 * @author David Earl<david@frankieandshadow.com> 35 35 * … … 37 37 public class PlayHeadMarker extends Marker { 38 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 super(new LatLon(0.0,0.0), "", 63 Main.pref.get("marker.audiotracericon", "audio-tracer"), 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 * called back from drag mode to say when we started dragging for real 95 96 97 98 if (timer != null) 99 100 101 102 try { AudioPlayer.pause(); } 103 104 105 106 107 108 * reinstate the old map mode after swuitching temporarily to do a play head drag 109 110 111 if (! wasPlaying || reset) 112 113 114 115 116 117 118 119 120 121 122 123 * @param en the new position in map terms 124 125 126 127 128 129 130 131 * Find the closest track point within the pixelTolerance of the screen point pNear 132 * @param pNear : the point in screen coordinates near which to find a track point 133 134 135 * 136 137 138 139 140 141 // Find the track point closest to letting go of the play head 142 143 144 if (trackLayer.data.tracks == null) 145 146 147 148 if (track.trackSegs == null) 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 /* work out EastNorth equivalent of 50 (default) pixels tolerance */ 179 180 181 182 183 184 AudioMarker ca = null; 185 /* Find the prior audio marker (there should always be one in the 186 * layer, even if it is only one at the start of the track) to 187 * offset the audio from */ 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 * Synchronize the audio at the position where the play head was paused before 214 * dragging with the position on the track where it was dropped. 215 * If this is quite near an audio marker, we use that 216 * marker as the sync. location, otherwise we create a new marker at the 217 * trackpoint nearest the end point of the drag point to apply the 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 * callback for moving play head marker according to audio player position 298 299 300 301 302 303 double audioTime = recentlyPlayedMarker.time + 304 AudioPlayer.position() - 305 306 307 308 309 310 311 312 313 314 * and interpolate between them 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 eastNorth = w2 == null ? 337 w1.eastNorth : 338 w1.eastNorth.interpolate(w2.eastNorth, 339 340 341 342 39 private Timer timer = null; 40 private double animationInterval = 0.0; // seconds 41 // private Rectangle audioTracer = null; 42 // private Icon audioTracerIcon = null; 43 static private PlayHeadMarker playHead = null; 44 private MapMode oldMode = null; 45 private EastNorth oldEastNorth; 46 private boolean enabled; 47 private boolean wasPlaying = false; 48 private int dropTolerance = 50; /* pixels */ 49 50 public static PlayHeadMarker create() { 51 if (playHead == null) { 52 try { 53 playHead = new PlayHeadMarker(); 54 } catch (Exception ex) { 55 return null; 56 } 57 } 58 return playHead; 59 } 60 61 private PlayHeadMarker() { 62 super(new LatLon(0.0,0.0), "", 63 Main.pref.get("marker.audiotracericon", "audio-tracer"), 64 null, -1.0, 0.0); 65 enabled = Main.pref.getBoolean("marker.traceaudio", true); 66 if (! enabled) return; 67 try { dropTolerance = Integer.parseInt(Main.pref.get("marker.playHeadDropTolerance", "50")); } 68 catch(NumberFormatException x) { dropTolerance = 50; } 69 Main.map.mapView.addMouseListener(new MouseAdapter() { 70 @Override public void mousePressed(MouseEvent ev) { 71 Point p = ev.getPoint(); 72 if (ev.getButton() != MouseEvent.BUTTON1 || p == null) 73 return; 74 if (playHead.containsPoint(p)) { 75 /* when we get a click on the marker, we need to switch mode to avoid 76 * getting confused with other drag operations (like select) */ 77 oldMode = Main.map.mapMode; 78 oldEastNorth = eastNorth; 79 PlayHeadDragMode playHeadDragMode = new PlayHeadDragMode(playHead); 80 Main.map.selectMapMode(playHeadDragMode); 81 playHeadDragMode.mousePressed(ev); 82 } 83 } 84 }); 85 } 86 87 @Override public boolean containsPoint(Point p) { 88 Point screen = Main.map.mapView.getPoint(eastNorth); 89 Rectangle r = new Rectangle(screen.x, screen.y, symbol.getIconWidth(), symbol.getIconHeight()); 90 return r.contains(p); 91 } 92 93 /** 94 * called back from drag mode to say when we started dragging for real 95 * (at least a short distance) 96 */ 97 public void startDrag() { 98 if (timer != null) 99 timer.stop(); 100 wasPlaying = AudioPlayer.playing(); 101 if (wasPlaying) { 102 try { AudioPlayer.pause(); } 103 catch (Exception ex) { AudioPlayer.audioMalfunction(ex);} 104 } 105 } 106 107 /** 108 * reinstate the old map mode after swuitching temporarily to do a play head drag 109 */ 110 private void endDrag(boolean reset) { 111 if (! wasPlaying || reset) 112 try { AudioPlayer.pause(); } 113 catch (Exception ex) { AudioPlayer.audioMalfunction(ex);} 114 if (reset) 115 eastNorth = oldEastNorth; 116 Main.map.selectMapMode(oldMode); 117 Main.map.mapView.repaint(); 118 timer.start(); 119 } 120 121 /** 122 * apply the new position resulting from a drag in progress 123 * @param en the new position in map terms 124 */ 125 public void drag(EastNorth en) { 126 eastNorth = en; 127 Main.map.mapView.repaint(); 128 } 129 130 /** 131 * Find the closest track point within the pixelTolerance of the screen point pNear 132 * @param pNear : the point in screen coordinates near which to find a track point 133 * @param pixelTolerance : only accept the point if within this number of pixels of en 134 * @return the nearest trackpoint or null if nothing nearby 135 * 136 * XXX seems unused, F.R. 2008-03-15 137 private WayPoint getClosestTrackPoint(Point pNear, double pixelTolerance) { 138 WayPoint cw = null; 139 AudioMarker recentlyPlayedMarker = AudioMarker.recentlyPlayedMarker(); 140 if (recentlyPlayedMarker != null) { 141 // Find the track point closest to letting go of the play head 142 double minDistance = pixelTolerance; 143 GpxLayer trackLayer = recentlyPlayedMarker.parentLayer.fromLayer; 144 if (trackLayer.data.tracks == null) 145 return null; 146 147 for (GpxTrack track : trackLayer.data.tracks) { 148 if (track.trackSegs == null) 149 continue; 150 151 for (Collection<WayPoint> trackseg : track.trackSegs) { 152 for (WayPoint w : trackseg) { 153 Point p = Main.map.mapView.getPoint(w.eastNorth); 154 double distance = p.distance(pNear); 155 if (distance <= minDistance) { 156 cw = w; 157 minDistance = distance; 158 } 159 } 160 } 161 } 162 } 163 return cw; 164 } 165 */ 166 167 /** 168 * reposition the play head at the point on the track nearest position given, 169 * providing we are within reasonable distance from the track; otherwise reset to the 170 * original position. 171 * @param en the position to start looking from 172 */ 173 public void reposition(EastNorth en) { 174 // eastNorth = en; 175 WayPoint cw = null; 176 AudioMarker recent = AudioMarker.recentlyPlayedMarker(); 177 if (recent != null && recent.parentLayer != null && recent.parentLayer.fromLayer != null) { 178 /* work out EastNorth equivalent of 50 (default) pixels tolerance */ 179 Point p = Main.map.mapView.getPoint(en); 180 EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y); 181 cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east()); 182 } 183 184 AudioMarker ca = null; 185 /* Find the prior audio marker (there should always be one in the 186 * layer, even if it is only one at the start of the track) to 187 * offset the audio from */ 188 if (cw != null) { 189 if (recent != null && recent.parentLayer != null) { 190 for (Marker m : recent.parentLayer.data) { 191 if (m instanceof AudioMarker) { 192 AudioMarker a = (AudioMarker) m; 193 if (a.time > cw.time) 194 break; 195 ca = a; 196 } 197 } 198 } 199 } 200 201 if (ca == null) { 202 /* Not close enough to track, or no audio marker found for some other reason */ 203 JOptionPane.showMessageDialog(Main.parent, tr("You need to Drag the play head near to the GPX track whose associated sound track you were playing.")); 204 endDrag(true); 205 } else { 206 eastNorth = cw.eastNorth; 207 ca.play(cw.time - ca.time); 208 endDrag(false); 209 } 210 } 211 212 /** 213 * Synchronize the audio at the position where the play head was paused before 214 * dragging with the position on the track where it was dropped. 215 * If this is quite near an audio marker, we use that 216 * marker as the sync. location, otherwise we create a new marker at the 217 * trackpoint nearest the end point of the drag point to apply the 218 * sync to. 219 * @param en : the EastNorth end point of the drag 220 */ 221 public void synchronize(EastNorth en) { 222 AudioMarker recent = AudioMarker.recentlyPlayedMarker(); 223 if(recent == null) 224 return; 225 /* First, see if we dropped onto an existing audio marker in the layer being played */ 226 Point startPoint = Main.map.mapView.getPoint(en); 227 AudioMarker ca = null; 228 if (recent.parentLayer != null) { 229 double closestAudioMarkerDistanceSquared = 1.0E100; 230 for (Marker m : recent.parentLayer.data) { 231 if (m instanceof AudioMarker) { 232 double distanceSquared = m.eastNorth.distanceSq(en); 233 if (distanceSquared < closestAudioMarkerDistanceSquared) { 234 ca = (AudioMarker) m; 235 closestAudioMarkerDistanceSquared = distanceSquared; 236 } 237 } 238 } 239 } 240 241 /* We found the closest marker: did we actually hit it? */ 242 if (ca != null && ! ca.containsPoint(startPoint)) ca = null; 243 244 /* If we didn't hit an audio marker, we need to create one at the nearest point on the track */ 245 if (ca == null) { 246 /* work out EastNorth equivalent of 50 (default) pixels tolerance */ 247 Point p = Main.map.mapView.getPoint(en); 248 EastNorth enPlus25px = Main.map.mapView.getEastNorth(p.x+dropTolerance, p.y); 249 WayPoint cw = recent.parentLayer.fromLayer.nearestPointOnTrack(en, enPlus25px.east() - en.east()); 250 if (cw == null) { 251 JOptionPane.showMessageDialog(Main.parent, tr("You need to SHIFT-Drag the play head onto an audio marker or onto the track point where you want to synchronize.")); 252 endDrag(true); 253 return; 254 } 255 ca = recent.parentLayer.addAudioMarker(cw.time, cw.eastNorth); 256 } 257 258 /* Actually do the synchronization */ 259 if(ca == null) 260 { 261 JOptionPane.showMessageDialog(Main.parent,tr("Unable to create new Audio marker.")); 262 endDrag(true); 263 } 264 else if (recent.parentLayer.synchronizeAudioMarkers(ca)) { 265 JOptionPane.showMessageDialog(Main.parent, tr("Audio synchronized at point {0}.", ca.text)); 266 eastNorth = ca.eastNorth; 267 endDrag(false); 268 } else { 269 JOptionPane.showMessageDialog(Main.parent,tr("Unable to synchronize in layer being played.")); 270 endDrag(true); 271 } 272 } 273 274 public void paint(Graphics g, MapView mv /*, boolean mousePressed */) { 275 if (time < 0.0) return; 276 Point screen = mv.getPoint(eastNorth); 277 symbol.paintIcon(mv, g, screen.x, screen.y); 278 } 279 280 public void animate() { 281 if (! enabled) return; 282 if (timer == null) { 283 animationInterval = Double.parseDouble(Main.pref.get("marker.audioanimationinterval", "1")); //milliseconds 284 timer = new Timer((int)(animationInterval * 1000.0), new ActionListener() { 285 public void actionPerformed(ActionEvent e) { 286 timerAction(); 287 } 288 }); 289 timer.setInitialDelay(0); 290 } else { 291 timer.stop(); 292 } 293 timer.start(); 294 } 295 296 /** 297 * callback for moving play head marker according to audio player position 298 */ 299 public void timerAction() { 300 AudioMarker recentlyPlayedMarker = AudioMarker.recentlyPlayedMarker(); 301 if (recentlyPlayedMarker == null) 302 return; 303 double audioTime = recentlyPlayedMarker.time + 304 AudioPlayer.position() - 305 recentlyPlayedMarker.offset - 306 recentlyPlayedMarker.syncOffset; 307 if (Math.abs(audioTime - time) < animationInterval) 308 return; 309 if (recentlyPlayedMarker.parentLayer == null) return; 310 GpxLayer trackLayer = recentlyPlayedMarker.parentLayer.fromLayer; 311 if (trackLayer == null) 312 return; 313 /* find the pair of track points for this position (adjusted by the syncOffset) 314 * and interpolate between them 315 */ 316 WayPoint w1 = null; 317 WayPoint w2 = null; 318 319 for (GpxTrack track : trackLayer.data.tracks) { 320 for (Collection<WayPoint> trackseg : track.trackSegs) { 321 for (Iterator<WayPoint> it = trackseg.iterator(); it.hasNext();) { 322 WayPoint w = it.next(); 323 if (audioTime < w.time) { 324 w2 = w; 325 break; 326 } 327 w1 = w; 328 } 329 if (w2 != null) break; 330 } 331 if (w2 != null) break; 332 } 333 334 if (w1 == null) 335 return; 336 eastNorth = w2 == null ? 337 w1.eastNorth : 338 w1.eastNorth.interpolate(w2.eastNorth, 339 (audioTime - w1.time)/(w2.time - w1.time)); 340 time = audioTime; 341 Main.map.mapView.repaint(); 342 } 343 343 } -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/WebMarker.java
r627 r1169 15 15 /** 16 16 * Marker class with Web URL activation. 17 * 17 * 18 18 * @author Frederik Ramm <frederik@remote.org> 19 19 * … … 21 21 public class WebMarker extends ButtonMarker { 22 22 23 23 public URL webUrl; 24 24 25 26 27 28 29 30 31 25 public static WebMarker create (LatLon ll, String url, MarkerLayer parentLayer, double time, double offset) { 26 try { 27 return new WebMarker(ll, new URL(url), parentLayer, time, offset); 28 } catch (Exception ex) { 29 return null; 30 } 31 } 32 32 33 34 35 36 33 private WebMarker(LatLon ll, URL webUrl, MarkerLayer parentLayer, double time, double offset) { 34 super(ll, "web.png", parentLayer, time, offset); 35 this.webUrl = webUrl; 36 } 37 37 38 39 40 41 JOptionPane.showMessageDialog(Main.parent, 42 "<html><b>" + 43 44 "</b><br>" + tr("(URL was: ") + webUrl.toString() + ")" + "<br>" + error, 45 46 47 38 @Override public void actionPerformed(ActionEvent ev) { 39 String error = OpenBrowser.displayUrl(webUrl.toString()); 40 if (error != null) { 41 JOptionPane.showMessageDialog(Main.parent, 42 "<html><b>" + 43 tr("There was an error while trying to display the URL for this marker") + 44 "</b><br>" + tr("(URL was: ") + webUrl.toString() + ")" + "<br>" + error, 45 tr("Error displaying URL"), JOptionPane.ERROR_MESSAGE); 46 } 47 } 48 48 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/AreaElemStyle.java
r885 r1169 4 4 public class AreaElemStyle extends ElemStyle 5 5 { 6 7 6 public Color color; 7 public LineElemStyle line = null; 8 8 9 10 11 12 13 14 9 public AreaElemStyle (AreaElemStyle a, long maxScale, long minScale) { 10 this.color = a.color; 11 this.priority = a.priority; 12 this.maxScale = maxScale; 13 this.minScale = minScale; 14 } 15 15 16 17 18 19 20 21 22 23 16 public AreaElemStyle(AreaElemStyle a, LineElemStyle l) 17 { 18 this.color = a.color; 19 this.priority = a.priority; 20 this.maxScale = a.maxScale; 21 this.minScale = a.minScale; 22 this.line = l; 23 } 24 24 25 25 public AreaElemStyle() { init(); } 26 26 27 28 29 30 31 27 public void init() 28 { 29 color = null; 30 priority = 0; 31 } 32 32 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java
r885 r1169 3 3 abstract public class ElemStyle 4 4 { 5 6 7 5 // zoom range to display the feature 6 public long minScale; 7 public long maxScale; 8 8 9 9 public int priority; 10 10 } 11 11 -
trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyleHandler.java
r999 r1169 11 11 public class ElemStyleHandler extends DefaultHandler 12 12 { 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 13 boolean inDoc, inRule, inCondition, inElemStyle, inLine, inLineMod, inIcon, inArea, inScaleMax, inScaleMin; 14 boolean hadLine, hadLineMod, hadIcon, hadArea; 15 ElemStyles styles; 16 String styleName; 17 RuleElem rule = new RuleElem(); 18 19 class RuleElem { 20 String key; 21 String value; 22 String boolValue; 23 long scaleMax; 24 long scaleMin; 25 LineElemStyle line = new LineElemStyle(); 26 LineElemStyle linemod = new LineElemStyle(); 27 AreaElemStyle area = new AreaElemStyle(); 28 IconElemStyle icon = new IconElemStyle(); 29 public void init() 30 { 31 key = value = boolValue = null; 32 scaleMax = 1000000000; 33 scaleMin = 0; 34 line.init(); 35 linemod.init(); 36 area.init(); 37 icon.init(); 38 } 39 } 40 41 public ElemStyleHandler(String name) { 42 styleName = name; 43 inDoc=inRule=inCondition=inElemStyle=inLine=inIcon=inArea=false; 44 rule.init(); 45 styles = MapPaintStyles.getStyles(); 46 } 47 48 Color convertColor(String colString) 49 { 50 int i = colString.indexOf("#"); 51 String colorString; 52 if(i < 0) // name only 53 colorString = Main.pref.get("color.mappaint."+styleName+"."+colString); 54 else if(i == 0) // value only 55 colorString = colString; 56 else // value and name 57 colorString = Main.pref.get("color.mappaint."+styleName+"."+colString.substring(0,i), colString.substring(i)); 58 return ColorHelper.html2color(colorString); 59 } 60 61 @Override public void startDocument() { 62 inDoc = true; 63 } 64 65 @Override public void endDocument() { 66 inDoc = false; 67 } 68 69 @Override public void startElement(String uri,String name, String qName, Attributes atts) { 70 if (inDoc==true) 71 { 72 if (qName.equals("rule")) 73 inRule=true; 74 else if (qName.equals("scale_max")) 75 inScaleMax = true; 76 else if (qName.equals("scale_min")) 77 inScaleMin = true; 78 else if (qName.equals("condition") && inRule) 79 { 80 inCondition=true; 81 for (int count=0; count<atts.getLength(); count++) 82 { 83 if(atts.getQName(count).equals("k")) 84 rule.key = atts.getValue(count); 85 else if(atts.getQName(count).equals("v")) 86 rule.value = atts.getValue(count); 87 else if(atts.getQName(count).equals("b")) 88 rule.boolValue = atts.getValue(count); 89 } 90 } 91 else if (qName.equals("line")) 92 { 93 hadLine = inLine = true; 94 for (int count=0; count<atts.getLength(); count++) 95 { 96 if(atts.getQName(count).equals("width")) 97 rule.line.width = Integer.parseInt(atts.getValue(count)); 98 else if (atts.getQName(count).equals("colour")) 99 rule.line.color=convertColor(atts.getValue(count)); 100 else if (atts.getQName(count).equals("realwidth")) 101 rule.line.realWidth=Integer.parseInt(atts.getValue(count)); 102 else if (atts.getQName(count).equals("dashed")) 103 rule.line.dashed=Boolean.parseBoolean(atts.getValue(count)); 104 else if(atts.getQName(count).equals("priority")) 105 rule.line.priority = Integer.parseInt(atts.getValue(count)); 106 } 107 } 108 else if (qName.equals("linemod")) 109 { 110 hadLineMod = inLineMod = true; 111 for (int count=0; count<atts.getLength(); count++) 112 { 113 if(atts.getQName(count).equals("width")) 114 { 115 String val = atts.getValue(count); 116 if(val.startsWith("+")) 117 { 118 rule.linemod.width = Integer.parseInt(val.substring(1)); 119 rule.linemod.widthMode = LineElemStyle.WidthMode.OFFSET; 120 } 121 else if(val.startsWith("-")) 122 { 123 rule.linemod.width = Integer.parseInt(val); 124 rule.linemod.widthMode = LineElemStyle.WidthMode.OFFSET; 125 } 126 else if(val.endsWith("%")) 127 { 128 rule.linemod.width = Integer.parseInt(val.substring(0, val.length()-1)); 129 rule.linemod.widthMode = LineElemStyle.WidthMode.PERCENT; 130 } 131 else 132 rule.linemod.width = Integer.parseInt(val); 133 } 134 else if (atts.getQName(count).equals("colour")) 135 rule.linemod.color=convertColor(atts.getValue(count)); 136 else if (atts.getQName(count).equals("realwidth")) 137 rule.linemod.realWidth=Integer.parseInt(atts.getValue(count)); 138 else if (atts.getQName(count).equals("dashed")) 139 rule.linemod.dashed=Boolean.parseBoolean(atts.getValue(count)); 140 else if(atts.getQName(count).equals("priority")) 141 rule.linemod.priority = Integer.parseInt(atts.getValue(count)); 142 else if(atts.getQName(count).equals("mode")) 143 rule.linemod.over = !atts.getValue(count).equals("under"); 144 } 145 } 146 else if (qName.equals("icon")) 147 { 148 hadIcon = inIcon = true; 149 for (int count=0; count<atts.getLength(); count++) 150 { 151 if (atts.getQName(count).equals("src")) 152 rule.icon.icon = MapPaintStyles.getIcon(atts.getValue(count), styleName); 153 else if (atts.getQName(count).equals("annotate")) 154 rule.icon.annotate = Boolean.parseBoolean (atts.getValue(count)); 155 else if(atts.getQName(count).equals("priority")) 156 rule.icon.priority = Integer.parseInt(atts.getValue(count)); 157 } 158 } 159 else if (qName.equals("area")) 160 { 161 hadArea = inArea = true; 162 for (int count=0; count<atts.getLength(); count++) 163 { 164 if (atts.getQName(count).equals("colour")) 165 rule.area.color=convertColor(atts.getValue(count)); 166 else if(atts.getQName(count).equals("priority")) 167 rule.area.priority = Integer.parseInt(atts.getValue(count)); 168 } 169 } 170 } 171 } 172 173 @Override public void endElement(String uri,String name, String qName) 174 { 175 if (inRule && qName.equals("rule")) 176 { 177 if(hadLine) 178 styles.add(styleName, rule.key, rule.value, rule.boolValue, 179 new LineElemStyle(rule.line, rule.scaleMax, rule.scaleMin)); 180 if(hadLineMod) 181 styles.addModifier(styleName, rule.key, rule.value, rule.boolValue, 182 new LineElemStyle(rule.linemod, rule.scaleMax, rule.scaleMin)); 183 if(hadIcon) 184 styles.add(styleName, rule.key, rule.value, rule.boolValue, 185 new IconElemStyle(rule.icon, rule.scaleMax, rule.scaleMin)); 186 if(hadArea) 187 styles.add(styleName, rule.key, rule.value, rule.boolValue, 188 new AreaElemStyle(rule.area, rule.scaleMax, rule.scaleMin)); 189 inRule = false; 190 hadLine = hadLineMod = hadIcon = hadArea = false; 191 rule.init(); 192 } 193 else if (inCondition && qName.equals("condition")) 194 inCondition = false; 195 else if (inLine && qName.equals("line")) 196 inLine = false; 197 else if (inLineMod && qName.equals("linemod")) 198 inLineMod = false; 199 else if (inIcon && qName.equals("icon")) 200 inIcon = false; 201 else if (inArea && qName.equals("area")) 202 inArea = false; 203 else if (qName.equals("scale_max")) 204 inScaleMax = false; 205 else if (qName.equals("scale_min")) 206 inScaleMin = false; 207 } 208 209 @Override public void characters(char ch[], int start, int length) 210 { 211 if (inScaleMax == true) 212 rule.scaleMax = Long.parseLong(new String(ch, start, length)); 213 else if (inScaleMin == true) 214 rule.scaleMin = Long.parseLong(new String(ch, start, length)); 215 } 216 216 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java
r999 r1169 14 14 public class ElemStyles 15 15 { 16 17 18 19 20 21 22 23 24 25 26 27 28 29 16 private class StyleSet { 17 HashMap<String, IconElemStyle> icons; 18 HashMap<String, LineElemStyle> lines; 19 HashMap<String, AreaElemStyle> areas; 20 HashMap<String, LineElemStyle> modifiers; 21 public StyleSet() 22 { 23 icons = new HashMap<String, IconElemStyle>(); 24 lines = new HashMap<String, LineElemStyle>(); 25 modifiers = new HashMap<String, LineElemStyle>(); 26 areas = new HashMap<String, AreaElemStyle>(); 27 } 28 } 29 HashMap<String, StyleSet> styleSet; 30 30 31 32 33 34 31 public ElemStyles() 32 { 33 styleSet = new HashMap<String, StyleSet>(); 34 } 35 35 36 37 38 39 40 41 42 43 44 36 private String getKey(String k, String v, String b) 37 { 38 if(v != null) 39 return "n" + k + "=" + v; 40 else if(b != null) 41 return "b" + k + "=" + OsmUtils.getNamedOsmBoolean(b); 42 else 43 return "x" + k; 44 } 45 45 46 47 48 49 46 public void add(String name, String k, String v, String b, LineElemStyle style) 47 { 48 getStyleSet(name, true).lines.put(getKey(k,v,b), style); 49 } 50 50 51 52 53 54 51 public void addModifier(String name, String k, String v, String b, LineElemStyle style) 52 { 53 getStyleSet(name, true).modifiers.put(getKey(k,v,b), style); 54 } 55 55 56 57 58 59 56 public void add(String name, String k, String v, String b, AreaElemStyle style) 57 { 58 getStyleSet(name, true).areas.put(getKey(k,v,b), style); 59 } 60 60 61 62 63 64 61 public void add(String name, String k, String v, String b, IconElemStyle style) 62 { 63 getStyleSet(name, true).icons.put(getKey(k,v,b), style); 64 } 65 65 66 67 68 69 70 71 72 73 74 75 76 77 66 private StyleSet getStyleSet(String name, boolean create) 67 { 68 if(name == null) 69 name = Main.pref.get("mappaint.style", "standard"); 70 StyleSet s = styleSet.get(name); 71 if(create && s == null) 72 { 73 s = new StyleSet(); 74 styleSet.put(name, s); 75 } 76 return s; 77 } 78 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 79 public IconElemStyle get(Node n) 80 { 81 StyleSet ss = getStyleSet(null, false); 82 IconElemStyle ret = null; 83 if(ss != null && n.keys != null) 84 { 85 Iterator<String> iterator = n.keys.keySet().iterator(); 86 while(iterator.hasNext()) 87 { 88 String key = iterator.next(); 89 String val = n.keys.get(key); 90 IconElemStyle style; 91 if((style = ss.icons.get("n" + key + "=" + val)) != null) 92 { 93 if(ret == null || style.priority > ret.priority) 94 ret = style; 95 } 96 if((style = ss.icons.get("b" + key + "=" + OsmUtils.getNamedOsmBoolean(val))) != null) 97 { 98 if(ret == null || style.priority > ret.priority) 99 ret = style; 100 } 101 if((style = ss.icons.get("x" + key)) != null) 102 { 103 if(ret == null || style.priority > ret.priority) 104 ret = style; 105 } 106 } 107 } 108 return ret; 109 } 110 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 111 public ElemStyle get(Way w) 112 { 113 StyleSet ss = getStyleSet(null, false); 114 if(ss == null || w.keys == null) 115 return null; 116 AreaElemStyle retArea = null; 117 LineElemStyle retLine = null; 118 String linestring = null; 119 HashMap<String, LineElemStyle> over = new HashMap<String, LineElemStyle>(); 120 Iterator<String> iterator = w.keys.keySet().iterator(); 121 while(iterator.hasNext()) 122 { 123 String key = iterator.next(); 124 String val = w.keys.get(key); 125 AreaElemStyle styleArea; 126 LineElemStyle styleLine; 127 String idx = "n" + key + "=" + val; 128 if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority)) 129 retArea = styleArea; 130 if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority)) 131 { 132 retLine = styleLine; 133 linestring = idx; 134 } 135 if((styleLine = ss.modifiers.get(idx)) != null) 136 over.put(idx, styleLine); 137 idx = "b" + key + "=" + OsmUtils.getNamedOsmBoolean(val); 138 if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority)) 139 retArea = styleArea; 140 if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority)) 141 { 142 retLine = styleLine; 143 linestring = idx; 144 } 145 if((styleLine = ss.modifiers.get(idx)) != null) 146 over.put(idx, styleLine); 147 idx = "x" + key; 148 if((styleArea = ss.areas.get(idx)) != null && (retArea == null || styleArea.priority > retArea.priority)) 149 retArea = styleArea; 150 if((styleLine = ss.lines.get(idx)) != null && (retLine == null || styleLine.priority > retLine.priority)) 151 { 152 retLine = styleLine; 153 linestring = idx; 154 } 155 if((styleLine = ss.modifiers.get(idx)) != null) 156 over.put(idx, styleLine); 157 } 158 over.remove(linestring); 159 if(over.size() != 0 && retLine != null) 160 { 161 List<LineElemStyle> s = new LinkedList<LineElemStyle>(over.values()); 162 Collections.sort(s); 163 retLine = new LineElemStyle(retLine, s); 164 } 165 if(retArea != null) 166 { 167 if(retLine != null) 168 return new AreaElemStyle(retArea, retLine); 169 else 170 return retArea; 171 } 172 return retLine; 173 } 174 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 175 public boolean isArea(Way w) 176 { 177 StyleSet ss = getStyleSet(null, false); 178 if(ss != null && w.keys != null) 179 { 180 Iterator<String> iterator = w.keys.keySet().iterator(); 181 while(iterator.hasNext()) 182 { 183 String key = iterator.next(); 184 String val = w.keys.get(key); 185 if(ss.areas.containsKey("n" + key + "=" + val) 186 || ss.areas.containsKey("n" + key + "=" + OsmUtils.getNamedOsmBoolean(val)) 187 || ss.areas.containsKey("x" + key)) 188 return true; 189 } 190 } 191 return false; 192 } 193 public boolean hasAreas() 194 { 195 StyleSet ss = getStyleSet(null, false); 196 return ss != null && ss.areas.size() > 0; 197 } 198 198 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/IconElemStyle.java
r885 r1169 4 4 public class IconElemStyle extends ElemStyle 5 5 { 6 7 6 public ImageIcon icon; 7 public boolean annotate; 8 8 9 10 11 12 13 14 15 16 9 public IconElemStyle (IconElemStyle i, long maxScale, long minScale) { 10 this.icon = i.icon; 11 this.annotate = i.annotate; 12 this.priority = i.priority; 13 this.maxScale = maxScale; 14 this.minScale = minScale; 15 } 16 public IconElemStyle() { init(); } 17 17 18 19 20 21 22 23 18 public void init() 19 { 20 icon = null; 21 priority = 0; 22 annotate = true; 23 } 24 24 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/LineElemStyle.java
r999 r1169 6 6 public class LineElemStyle extends ElemStyle implements Comparable<LineElemStyle> 7 7 { 8 9 10 11 8 public int width; 9 public int realWidth; //the real width of this line in meter 10 public Color color; 11 public boolean dashed; 12 12 13 14 15 13 public boolean over; 14 public enum WidthMode { ABSOLUTE, PERCENT, OFFSET } 15 public WidthMode widthMode; 16 16 17 17 public Collection<LineElemStyle> overlays; 18 18 19 20 21 22 23 24 25 19 public LineElemStyle(LineElemStyle s, long maxScale, long minScale) { 20 this.width = s.width; 21 this.realWidth = s.realWidth; 22 this.color = s.color; 23 this.dashed = s.dashed; 24 this.over = s.over; 25 this.widthMode = s.widthMode; 26 26 27 28 29 30 27 this.priority = s.priority; 28 this.maxScale = maxScale; 29 this.minScale = minScale; 30 } 31 31 32 33 34 35 36 37 38 32 public LineElemStyle(LineElemStyle s, Collection<LineElemStyle> overlays) { 33 this.width = s.width; 34 this.realWidth = s.realWidth; 35 this.color = s.color; 36 this.dashed = s.dashed; 37 this.over = s.over; 38 this.widthMode = s.widthMode; 39 39 40 41 42 40 this.priority = s.priority; 41 this.maxScale = s.maxScale; 42 this.minScale = s.minScale; 43 43 44 45 44 this.overlays = overlays; 45 } 46 46 47 47 public LineElemStyle() { init(); } 48 48 49 50 51 52 53 54 55 56 57 58 59 49 public void init() 50 { 51 width = 1; 52 realWidth = 0; 53 dashed = false; 54 priority = 0; 55 color = null; 56 over = true; // only used for line modifications 57 widthMode = WidthMode.ABSOLUTE; 58 overlays = null; 59 } 60 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 61 // get width for overlays 62 public int getWidth(int ref) 63 { 64 int res; 65 if(widthMode == WidthMode.ABSOLUTE) 66 res = width; 67 else if(widthMode == WidthMode.OFFSET) 68 res = ref + width; 69 else 70 { 71 if(width < 0) 72 res = 0; 73 else 74 res = ref*width/100; 75 } 76 return res <= 0 ? 1 : res; 77 } 78 78 79 80 81 82 83 84 85 86 87 79 public int compareTo(LineElemStyle s) 80 { 81 if(s.priority != priority) 82 return s.priority > priority ? 1 : -1; 83 if(!over && s.over) 84 return -1; 85 // we have no idea how to order other objects :-) 86 return 0; 87 } 88 88 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java
r999 r1169 16 16 public class MapPaintStyles { 17 17 18 private static ElemStyles styles = new ElemStyles(); 19 private static String iconDirs; 20 21 public static ElemStyles getStyles() 22 { 23 return styles; 24 } 18 private static ElemStyles styles = new ElemStyles(); 19 private static String iconDirs; 25 20 26 public static ImageIcon getIcon(String name, String styleName) 27 { 28 List<String> dirs = new LinkedList<String>(); 29 for(String fileset : iconDirs.split(";")) 30 { 31 String[] a; 32 if(fileset.indexOf("=") >= 0) 33 a = fileset.split("=", 2); 34 else 35 a = new String[] {"", fileset}; 21 public static ElemStyles getStyles() 22 { 23 return styles; 24 } 36 25 37 /* non-prefixed path is generic path, always take it */ 38 if(a[0].length() == 0 || styleName.equals(a[0])) 39 dirs.add(a[1]); 40 } 41 ImageIcon i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, name); 42 if(i == null) 43 { 44 System.out.println("Mappaint-Style \""+styleName+"\" icon \"" + name + "\" not found."); 45 i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, "misc/no_icon.png"); 46 } 47 return i; 48 } 26 public static ImageIcon getIcon(String name, String styleName) 27 { 28 List<String> dirs = new LinkedList<String>(); 29 for(String fileset : iconDirs.split(";")) 30 { 31 String[] a; 32 if(fileset.indexOf("=") >= 0) 33 a = fileset.split("=", 2); 34 else 35 a = new String[] {"", fileset}; 49 36 50 public static void readFromPreferences() { 51 /* don't prefix icon path, as it should be generic */ 52 String internalicon = "resource://images/styles/standard/;resource://images/styles/"; 53 String internalfile = "standard=resource://styles/standard/elemstyles.xml"; 37 /* non-prefixed path is generic path, always take it */ 38 if(a[0].length() == 0 || styleName.equals(a[0])) 39 dirs.add(a[1]); 40 } 41 ImageIcon i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, name); 42 if(i == null) 43 { 44 System.out.println("Mappaint-Style \""+styleName+"\" icon \"" + name + "\" not found."); 45 i = ImageProvider.getIfAvailable(dirs, "mappaint."+styleName, null, "misc/no_icon.png"); 46 } 47 return i; 48 } 54 49 55 iconDirs = Main.pref.get("mappaint.iconpaths"); 56 iconDirs = iconDirs == null || iconDirs.length() == 0 ? internalicon : iconDirs + ";" + internalicon; 50 public static void readFromPreferences() { 51 /* don't prefix icon path, as it should be generic */ 52 String internalicon = "resource://images/styles/standard/;resource://images/styles/"; 53 String internalfile = "standard=resource://styles/standard/elemstyles.xml"; 57 54 58 String file = Main.pref.get("mappaint.sources");59 file = file == null || file.length() == 0 ? internalfile : internalfile + ";" + file;55 iconDirs = Main.pref.get("mappaint.iconpaths"); 56 iconDirs = iconDirs == null || iconDirs.length() == 0 ? internalicon : iconDirs + ";" + internalicon; 60 57 61 for(String fileset : file.split(";")) 62 { 63 try 64 { 65 String[] a; 66 if(fileset.indexOf("=") >= 0) 67 a = fileset.split("=", 2); 68 else 69 a = new String[] {"standard", fileset}; 70 XMLReader xmlReader = XMLReaderFactory.createXMLReader(); 71 ElemStyleHandler handler = new ElemStyleHandler(a[0]); 72 xmlReader.setContentHandler(handler); 73 xmlReader.setErrorHandler(handler); 74 xmlReader.parse(new InputSource(new MirroredInputStream(a[1]))); 75 } 76 catch (Exception e) 77 { 78 System.out.println("Mappaint-Style problems: \"" + fileset + "\""); 79 } 80 } 81 iconDirs = null; 82 } 58 String file = Main.pref.get("mappaint.sources"); 59 file = file == null || file.length() == 0 ? internalfile : internalfile + ";" + file; 60 61 for(String fileset : file.split(";")) 62 { 63 try 64 { 65 String[] a; 66 if(fileset.indexOf("=") >= 0) 67 a = fileset.split("=", 2); 68 else 69 a = new String[] {"standard", fileset}; 70 XMLReader xmlReader = XMLReaderFactory.createXMLReader(); 71 ElemStyleHandler handler = new ElemStyleHandler(a[0]); 72 xmlReader.setContentHandler(handler); 73 xmlReader.setErrorHandler(handler); 74 xmlReader.parse(new InputSource(new MirroredInputStream(a[1]))); 75 } 76 catch (Exception e) 77 { 78 System.out.println("Mappaint-Style problems: \"" + fileset + "\""); 79 } 80 } 81 iconDirs = null; 82 } 83 83 84 84 } -
trunk/src/org/openstreetmap/josm/gui/preferences/AdvancedPreference.java
r1053 r1169 31 31 public class AdvancedPreference implements PreferenceSetting { 32 32 33 34 35 33 private Map<String,String> orig; 34 private Map<String,String> defaults; 35 private DefaultTableModel model; 36 36 37 38 JPanel p = gui.createPreferenceTab("advanced", tr("Advanced Preferences"), 39 37 public void addGui(final PreferenceDialog gui) { 38 JPanel p = gui.createPreferenceTab("advanced", tr("Advanced Preferences"), 39 tr("Setting Preference entries directly. Use with caution!"), false); 40 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 41 model = new DefaultTableModel(new String[]{tr("Key"), tr("Value")},0) { 42 @Override public boolean isCellEditable(int row, int column) { 43 return column != 0; 44 } 45 }; 46 DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(){ 47 public Component getTableCellRendererComponent(JTable table, Object value, 48 boolean isSelected, boolean hasFocus, int row, int column) 49 { 50 JLabel label=new JLabel(); 51 String s = defaults.get(value); 52 if(s != null) 53 { 54 if(s.equals(model.getValueAt(row, 1))) 55 label.setToolTipText(tr("Current value is default.")); 56 else 57 label.setToolTipText(tr("Default value is ''{0}''.", s)); 58 } 59 else 60 label.setToolTipText(tr("Default value currently unknown (setting has not been used yet).")); 61 label.setText((String)value); 62 return label; 63 } 64 }; 65 final JTable list = new JTable(model); 66 list.getColumn(tr("Key")).setCellRenderer(renderer); 67 JScrollPane scroll = new JScrollPane(list); 68 p.add(scroll, GBC.eol().fill(GBC.BOTH)); 69 scroll.setPreferredSize(new Dimension(400,200)); 70 70 71 72 73 74 75 76 77 78 79 80 71 orig = Main.pref.getAllPrefix(""); 72 defaults = Main.pref.getDefaults(); 73 orig.remove("osm-server.password"); 74 defaults.remove("osm-server.password"); 75 TreeSet<String> ts = new TreeSet<String>(orig.keySet()); 76 for (String s : defaults.keySet()) 77 { 78 if(!ts.contains(s)) 79 ts.add(s); 80 } 81 81 82 83 84 85 86 87 82 for (String s : ts) 83 { 84 String val = Main.pref.get(s); 85 if(val == null) val = ""; 86 model.addRow(new String[]{s, val}); 87 } 88 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 89 JButton add = new JButton(tr("Add")); 90 p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); 91 p.add(add, GBC.std().insets(0,5,0,0)); 92 add.addActionListener(new ActionListener(){ 93 public void actionPerformed(ActionEvent e) { 94 JPanel p = new JPanel(new GridBagLayout()); 95 p.add(new JLabel(tr("Key")), GBC.std().insets(0,0,5,0)); 96 JTextField key = new JTextField(10); 97 JTextField value = new JTextField(10); 98 p.add(key, GBC.eop().insets(5,0,0,0).fill(GBC.HORIZONTAL)); 99 p.add(new JLabel(tr("Value")), GBC.std().insets(0,0,5,0)); 100 p.add(value, GBC.eol().insets(5,0,0,0).fill(GBC.HORIZONTAL)); 101 int answer = JOptionPane.showConfirmDialog(gui, p, tr("Enter a new key/value pair"), JOptionPane.OK_CANCEL_OPTION); 102 if (answer == JOptionPane.OK_OPTION) 103 model.addRow(new String[]{key.getText(), value.getText()}); 104 } 105 }); 106 106 107 108 109 110 111 112 113 107 JButton edit = new JButton(tr("Edit")); 108 p.add(edit, GBC.std().insets(5,5,5,0)); 109 edit.addActionListener(new ActionListener(){ 110 public void actionPerformed(ActionEvent e) { 111 edit(gui, list); 112 } 113 }); 114 114 115 116 117 118 119 120 121 122 123 124 125 126 115 JButton delete = new JButton(tr("Delete")); 116 p.add(delete, GBC.std().insets(0,5,0,0)); 117 delete.addActionListener(new ActionListener(){ 118 public void actionPerformed(ActionEvent e) { 119 if (list.getSelectedRowCount() == 0) { 120 JOptionPane.showMessageDialog(gui, tr("Please select the row to delete.")); 121 return; 122 } 123 for(int row: list.getSelectedRows()) 124 model.setValueAt("", row, 1); 125 } 126 }); 127 127 128 129 130 131 132 133 134 128 list.addMouseListener(new MouseAdapter(){ 129 @Override public void mouseClicked(MouseEvent e) { 130 if (e.getClickCount() == 2) 131 edit(gui, list); 132 } 133 }); 134 } 135 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 136 public void ok() { 137 for (int i = 0; i < model.getRowCount(); ++i) { 138 String value = model.getValueAt(i,1).toString(); 139 if(value.length() != 0) 140 { 141 String key = model.getValueAt(i,0).toString(); 142 String origValue = orig.get(key); 143 if (origValue == null || !origValue.equals(value)) 144 Main.pref.put(key, value); 145 orig.remove(key); // processed. 146 } 147 } 148 for (Entry<String, String> e : orig.entrySet()) 149 Main.pref.put(e.getKey(), null); 150 } 151 151 152 152 153 154 155 156 157 158 159 160 161 153 private void edit(final PreferenceDialog gui, final JTable list) { 154 if (list.getSelectedRowCount() != 1) { 155 JOptionPane.showMessageDialog(gui, tr("Please select the row to edit.")); 156 return; 157 } 158 String v = JOptionPane.showInputDialog(tr("New value for {0}", model.getValueAt(list.getSelectedRow(), 0)), model.getValueAt(list.getSelectedRow(), 1)); 159 if (v != null) 160 model.setValueAt(v, list.getSelectedRow(), 1); 161 } 162 162 } -
trunk/src/org/openstreetmap/josm/gui/preferences/AudioPreference.java
r813 r1169 27 27 28 28 public class AudioPreference implements PreferenceSetting { 29 30 31 32 29 private JCheckBox audioMenuVisible = new JCheckBox(tr("Display the Audio menu.")); 30 private JCheckBox markerButtonLabels = new JCheckBox(tr("Label audio (and image and web) markers.")); 31 private JCheckBox markerAudioTraceVisible = new JCheckBox(tr("Display live audio trace.")); 32 private JCheckBox makeAutoMarkers = new JCheckBox(tr("Create non-audio markers when reading GPX.")); 33 33 34 35 36 37 38 34 // various methods of making markers on import audio 35 private JCheckBox audioMarkersFromExplicitWaypoints = new JCheckBox(tr("Explicit waypoints with valid timestamps.")); 36 private JCheckBox audioMarkersFromUntimedWaypoints = new JCheckBox(tr("Explicit waypoints with time estimated from track position.")); 37 private JCheckBox audioMarkersFromNamedTrackpoints = new JCheckBox(tr("Named trackpoints.")); 38 private JCheckBox audioMarkersFromStart = new JCheckBox(tr("Start of track (will always do this if no other markers available).")); 39 39 40 41 42 43 40 private JTextField audioLeadIn = new JTextField(8); 41 private JTextField audioForwardBackAmount = new JTextField(8); 42 private JTextField audioFastForwardMultiplier = new JTextField(8); 43 private JTextField audioCalibration = new JTextField(8); 44 44 45 46 47 48 49 50 51 52 53 54 55 56 45 public void addGui(PreferenceDialog gui) { 46 // audioMenuVisible 47 audioMenuVisible.addActionListener(new ActionListener(){ 48 public void actionPerformed(ActionEvent e) { 49 if (!audioMenuVisible.isSelected()) 50 audioMenuVisible.setSelected(false); 51 audioMenuVisible.setEnabled(audioMenuVisible.isSelected()); 52 } 53 }); 54 audioMenuVisible.setSelected(! Main.pref.getBoolean("audio.menuinvisible")); 55 audioMenuVisible.setToolTipText(tr("Show or hide the audio menu entry on the main menu bar.")); 56 gui.audio.add(audioMenuVisible, GBC.eol().insets(0,0,0,0)); 57 57 58 // audioTraceVisible 59 markerAudioTraceVisible.addActionListener(new ActionListener(){ 60 public void actionPerformed(ActionEvent e) { 61 if (!markerAudioTraceVisible.isSelected()) 62 markerAudioTraceVisible.setSelected(false); 63 } 64 }); 65 markerAudioTraceVisible.setSelected(Main.pref.getBoolean("marker.traceaudio", true)); 66 markerAudioTraceVisible.setToolTipText(tr("Display a moving icon representing the point on the synchronized track where the audio currently playing was recorded.")); 67 gui.audio.add(markerAudioTraceVisible, GBC.eol().insets(0,0,0,0)); 68 69 // buttonLabels 70 markerButtonLabels.addActionListener(new ActionListener(){ 71 public void actionPerformed(ActionEvent e) { 72 if (!markerButtonLabels.isSelected()) 73 markerButtonLabels.setSelected(false); 74 } 75 }); 76 markerButtonLabels.setSelected(Main.pref.getBoolean("marker.buttonlabels")); 77 markerButtonLabels.setToolTipText(tr("Put text labels against audio (and image and web) markers as well as their button icons.")); 78 gui.audio.add(markerButtonLabels, GBC.eol().insets(0,0,0,0)); 79 80 // makeAutoMarkers 81 makeAutoMarkers.addActionListener(new ActionListener(){ 82 public void actionPerformed(ActionEvent e) { 83 if (!makeAutoMarkers.isSelected()) 84 makeAutoMarkers.setSelected(false); 85 } 86 }); 87 makeAutoMarkers.setSelected(Main.pref.getBoolean("marker.makeautomarkers", true)); 88 makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer.")); 89 gui.audio.add(makeAutoMarkers, GBC.eol().insets(0,0,0,0)); 90 91 gui.audio.add(new JLabel(tr("When importing audio, make markers from...")), GBC.eol()); 58 // audioTraceVisible 59 markerAudioTraceVisible.addActionListener(new ActionListener(){ 60 public void actionPerformed(ActionEvent e) { 61 if (!markerAudioTraceVisible.isSelected()) 62 markerAudioTraceVisible.setSelected(false); 63 } 64 }); 65 markerAudioTraceVisible.setSelected(Main.pref.getBoolean("marker.traceaudio", true)); 66 markerAudioTraceVisible.setToolTipText(tr("Display a moving icon representing the point on the synchronized track where the audio currently playing was recorded.")); 67 gui.audio.add(markerAudioTraceVisible, GBC.eol().insets(0,0,0,0)); 92 68 93 // audioMarkersFromExplicitWaypoints 94 audioMarkersFromExplicitWaypoints.addActionListener(new ActionListener(){ 95 public void actionPerformed(ActionEvent e) { 96 if (!audioMarkersFromExplicitWaypoints.isSelected()) 97 audioMarkersFromExplicitWaypoints.setSelected(false); 98 } 99 }); 100 audioMarkersFromExplicitWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true)); 101 audioMarkersFromExplicitWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer.")); 102 gui.audio.add(audioMarkersFromExplicitWaypoints, GBC.eol().insets(10,0,0,0)); 103 104 // audioMarkersFromUntimedWaypoints 105 audioMarkersFromUntimedWaypoints.addActionListener(new ActionListener(){ 106 public void actionPerformed(ActionEvent e) { 107 if (!audioMarkersFromUntimedWaypoints.isSelected()) 108 audioMarkersFromUntimedWaypoints.setSelected(false); 109 } 110 }); 111 audioMarkersFromUntimedWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true)); 112 audioMarkersFromUntimedWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer.")); 113 gui.audio.add(audioMarkersFromUntimedWaypoints, GBC.eol().insets(10,0,0,0)); 114 115 // audioMarkersFromNamedTrackpoints 116 audioMarkersFromNamedTrackpoints.addActionListener(new ActionListener(){ 117 public void actionPerformed(ActionEvent e) { 118 if (!audioMarkersFromNamedTrackpoints.isSelected()) 119 audioMarkersFromNamedTrackpoints.setSelected(false); 120 } 121 }); 122 audioMarkersFromNamedTrackpoints.setSelected(Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)); 123 audioMarkersFromNamedTrackpoints.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions.")); 124 gui.audio.add(audioMarkersFromNamedTrackpoints, GBC.eol().insets(10,0,0,0)); 125 126 // audioMarkersFromStart 127 audioMarkersFromStart.addActionListener(new ActionListener(){ 128 public void actionPerformed(ActionEvent e) { 129 if (!audioMarkersFromStart.isSelected()) 130 audioMarkersFromStart.setSelected(false); 131 } 132 }); 133 audioMarkersFromStart.setSelected(Main.pref.getBoolean("marker.audiofromstart")); 134 audioMarkersFromStart.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions.")); 135 gui.audio.add(audioMarkersFromStart, GBC.eol().insets(10,0,0,0)); 136 137 audioForwardBackAmount.setText(Main.pref.get("audio.forwardbackamount", "10.0")); 138 audioForwardBackAmount.setToolTipText(tr("The number of seconds to jump forward or back when the relevant button is pressed")); 139 gui.audio.add(new JLabel(tr("Forward/back time (seconds)")), GBC.std()); 140 gui.audio.add(audioForwardBackAmount, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 69 // buttonLabels 70 markerButtonLabels.addActionListener(new ActionListener(){ 71 public void actionPerformed(ActionEvent e) { 72 if (!markerButtonLabels.isSelected()) 73 markerButtonLabels.setSelected(false); 74 } 75 }); 76 markerButtonLabels.setSelected(Main.pref.getBoolean("marker.buttonlabels")); 77 markerButtonLabels.setToolTipText(tr("Put text labels against audio (and image and web) markers as well as their button icons.")); 78 gui.audio.add(markerButtonLabels, GBC.eol().insets(0,0,0,0)); 141 79 142 audioFastForwardMultiplier.setText(Main.pref.get("audio.fastfwdmultiplier", "1.3")); 143 audioFastForwardMultiplier.setToolTipText(tr("The amount by which the speed is multiplied for fast forwarding")); 144 gui.audio.add(new JLabel(tr("Fast forward multiplier")), GBC.std()); 145 gui.audio.add(audioFastForwardMultiplier, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 80 // makeAutoMarkers 81 makeAutoMarkers.addActionListener(new ActionListener(){ 82 public void actionPerformed(ActionEvent e) { 83 if (!makeAutoMarkers.isSelected()) 84 makeAutoMarkers.setSelected(false); 85 } 86 }); 87 makeAutoMarkers.setSelected(Main.pref.getBoolean("marker.makeautomarkers", true)); 88 makeAutoMarkers.setToolTipText(tr("Automatically make a marker layer from any waypoints when opening a GPX layer.")); 89 gui.audio.add(makeAutoMarkers, GBC.eol().insets(0,0,0,0)); 146 90 147 audioLeadIn.setText(Main.pref.get("audio.leadin", "1")); 148 audioLeadIn.setToolTipText(tr("Playback starts this number of seconds before (or after, if negative) the audio track position requested")); 149 gui.audio.add(new JLabel(tr("Lead-in time (seconds)")), GBC.std()); 150 gui.audio.add(audioLeadIn, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 91 gui.audio.add(new JLabel(tr("When importing audio, make markers from...")), GBC.eol()); 151 92 152 audioCalibration.setText(Main.pref.get("audio.calibration", "1.0")); 153 audioCalibration.setToolTipText(tr("The ratio of voice recorder elapsed time to true elapsed time")); 154 gui.audio.add(new JLabel(tr("Voice recorder calibration")), GBC.std()); 155 gui.audio.add(audioCalibration, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 93 // audioMarkersFromExplicitWaypoints 94 audioMarkersFromExplicitWaypoints.addActionListener(new ActionListener(){ 95 public void actionPerformed(ActionEvent e) { 96 if (!audioMarkersFromExplicitWaypoints.isSelected()) 97 audioMarkersFromExplicitWaypoints.setSelected(false); 98 } 99 }); 100 audioMarkersFromExplicitWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true)); 101 audioMarkersFromExplicitWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer.")); 102 gui.audio.add(audioMarkersFromExplicitWaypoints, GBC.eol().insets(10,0,0,0)); 156 103 157 gui.audio.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL)); 158 } 104 // audioMarkersFromUntimedWaypoints 105 audioMarkersFromUntimedWaypoints.addActionListener(new ActionListener(){ 106 public void actionPerformed(ActionEvent e) { 107 if (!audioMarkersFromUntimedWaypoints.isSelected()) 108 audioMarkersFromUntimedWaypoints.setSelected(false); 109 } 110 }); 111 audioMarkersFromUntimedWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true)); 112 audioMarkersFromUntimedWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer.")); 113 gui.audio.add(audioMarkersFromUntimedWaypoints, GBC.eol().insets(10,0,0,0)); 159 114 160 public void ok() { 161 Main.pref.put("audio.menuinvisible", ! audioMenuVisible.isSelected()); 162 Main.pref.put("marker.traceaudio", markerAudioTraceVisible.isSelected()); 163 Main.pref.put("marker.buttonlabels", markerButtonLabels.isSelected()); 164 Main.pref.put("marker.makeautomarkers", makeAutoMarkers.isSelected()); 165 Main.pref.put("marker.audiofromexplicitwaypoints", audioMarkersFromExplicitWaypoints.isSelected()); 166 Main.pref.put("marker.audiofromuntimedwaypoints", audioMarkersFromUntimedWaypoints.isSelected()); 167 Main.pref.put("marker.audiofromnamedtrackpoints", audioMarkersFromNamedTrackpoints.isSelected()); 168 Main.pref.put("marker.audiofromstart", audioMarkersFromStart.isSelected()); 169 Main.pref.put("audio.forwardbackamount", audioForwardBackAmount.getText()); 170 Main.pref.put("audio.fastfwdmultiplier", audioFastForwardMultiplier.getText()); 171 Main.pref.put("audio.leadin", audioLeadIn.getText()); 172 Main.pref.put("audio.calibration", audioCalibration.getText()); 115 // audioMarkersFromNamedTrackpoints 116 audioMarkersFromNamedTrackpoints.addActionListener(new ActionListener(){ 117 public void actionPerformed(ActionEvent e) { 118 if (!audioMarkersFromNamedTrackpoints.isSelected()) 119 audioMarkersFromNamedTrackpoints.setSelected(false); 120 } 121 }); 122 audioMarkersFromNamedTrackpoints.setSelected(Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)); 123 audioMarkersFromNamedTrackpoints.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions.")); 124 gui.audio.add(audioMarkersFromNamedTrackpoints, GBC.eol().insets(10,0,0,0)); 125 126 // audioMarkersFromStart 127 audioMarkersFromStart.addActionListener(new ActionListener(){ 128 public void actionPerformed(ActionEvent e) { 129 if (!audioMarkersFromStart.isSelected()) 130 audioMarkersFromStart.setSelected(false); 131 } 132 }); 133 audioMarkersFromStart.setSelected(Main.pref.getBoolean("marker.audiofromstart")); 134 audioMarkersFromStart.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions.")); 135 gui.audio.add(audioMarkersFromStart, GBC.eol().insets(10,0,0,0)); 136 137 audioForwardBackAmount.setText(Main.pref.get("audio.forwardbackamount", "10.0")); 138 audioForwardBackAmount.setToolTipText(tr("The number of seconds to jump forward or back when the relevant button is pressed")); 139 gui.audio.add(new JLabel(tr("Forward/back time (seconds)")), GBC.std()); 140 gui.audio.add(audioForwardBackAmount, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 141 142 audioFastForwardMultiplier.setText(Main.pref.get("audio.fastfwdmultiplier", "1.3")); 143 audioFastForwardMultiplier.setToolTipText(tr("The amount by which the speed is multiplied for fast forwarding")); 144 gui.audio.add(new JLabel(tr("Fast forward multiplier")), GBC.std()); 145 gui.audio.add(audioFastForwardMultiplier, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 146 147 audioLeadIn.setText(Main.pref.get("audio.leadin", "1")); 148 audioLeadIn.setToolTipText(tr("Playback starts this number of seconds before (or after, if negative) the audio track position requested")); 149 gui.audio.add(new JLabel(tr("Lead-in time (seconds)")), GBC.std()); 150 gui.audio.add(audioLeadIn, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 151 152 audioCalibration.setText(Main.pref.get("audio.calibration", "1.0")); 153 audioCalibration.setToolTipText(tr("The ratio of voice recorder elapsed time to true elapsed time")); 154 gui.audio.add(new JLabel(tr("Voice recorder calibration")), GBC.std()); 155 gui.audio.add(audioCalibration, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 156 157 gui.audio.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL)); 158 } 159 160 public void ok() { 161 Main.pref.put("audio.menuinvisible", ! audioMenuVisible.isSelected()); 162 Main.pref.put("marker.traceaudio", markerAudioTraceVisible.isSelected()); 163 Main.pref.put("marker.buttonlabels", markerButtonLabels.isSelected()); 164 Main.pref.put("marker.makeautomarkers", makeAutoMarkers.isSelected()); 165 Main.pref.put("marker.audiofromexplicitwaypoints", audioMarkersFromExplicitWaypoints.isSelected()); 166 Main.pref.put("marker.audiofromuntimedwaypoints", audioMarkersFromUntimedWaypoints.isSelected()); 167 Main.pref.put("marker.audiofromnamedtrackpoints", audioMarkersFromNamedTrackpoints.isSelected()); 168 Main.pref.put("marker.audiofromstart", audioMarkersFromStart.isSelected()); 169 Main.pref.put("audio.forwardbackamount", audioForwardBackAmount.getText()); 170 Main.pref.put("audio.fastfwdmultiplier", audioFastForwardMultiplier.getText()); 171 Main.pref.put("audio.leadin", audioLeadIn.getText()); 172 Main.pref.put("audio.calibration", audioCalibration.getText()); 173 173 } 174 174 } -
trunk/src/org/openstreetmap/josm/gui/preferences/ColorPreference.java
r1165 r1169 40 40 public class ColorPreference implements PreferenceSetting { 41 41 42 43 42 private DefaultTableModel tableModel; 43 private JTable colors; 44 44 45 /** 46 * Set the colors to be shown in the preference table. This method creates a table model if 47 * none exists and overwrites all existing values. 48 * @param colorMap the map holding the colors 49 * (key = color id (without prefixes, so only <code>background</code>; not <code>color.background</code>), 50 * value = html representation of the color. 51 */ 52 public void setColorModel(Map<String, String> colorMap) { 53 if(tableModel == null) { 54 tableModel = new DefaultTableModel(); 55 tableModel.addColumn(tr("Color")); 56 tableModel.addColumn(tr("Name")); 57 } 58 59 // clear old model: 60 while(tableModel.getRowCount() > 0) { 61 tableModel.removeRow(0); 62 } 63 // fill model with colors: 64 List<String> colorKeyList = new ArrayList<String>(); 65 for(String key : colorMap.keySet()) { 66 colorKeyList.add(key); 67 } 68 Collections.sort(colorKeyList); 69 for (String key : colorKeyList) { 70 Vector<Object> row = new Vector<Object>(2); 71 row.add(key); 72 row.add(ColorHelper.html2color(colorMap.get(key))); 73 tableModel.addRow(row); 74 } 75 if(this.colors != null) { 76 this.colors.repaint(); 77 } 78 } 79 80 /** 81 * Returns a map with the colors in the table (key = color name without prefix, value = html color code). 82 * @return a map holding the colors. 83 */ 84 public Map<String, String> getColorModel() { 85 String key; 86 String value; 87 Map<String, String> colorMap = new HashMap<String, String>(); 88 for(int row = 0; row < tableModel.getRowCount(); ++row) { 89 key = (String)tableModel.getValueAt(row, 0); 90 value = ColorHelper.color2html((Color)tableModel.getValueAt(row, 1)); 91 colorMap.put(key, value); 92 } 93 return colorMap; 94 } 95 96 public void addGui(final PreferenceDialog gui) { 97 // initial fill with colors from preferences: 98 Map<String,String> prefColorMap = new TreeMap<String, String>(Main.pref.getAllPrefix("color.")); 99 fixColorPrefixes(prefColorMap); 100 Map<String,String> colorMap = new TreeMap<String, String>(); 101 for(String key : prefColorMap.keySet()) { 102 colorMap.put(key.substring("color.".length()), prefColorMap.get(key)); 103 } 104 setColorModel(colorMap); 105 106 colors = new JTable(tableModel) { 107 @Override public boolean isCellEditable(int row, int column) { 108 return false; 109 } 110 }; 111 colors.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 112 final TableCellRenderer oldColorsRenderer = colors.getDefaultRenderer(Object.class); 113 colors.setDefaultRenderer(Object.class, new TableCellRenderer(){ 114 public Component getTableCellRendererComponent(JTable t, Object o, boolean selected, boolean focus, int row, int column) { 115 if (column == 1) { 116 JLabel l = new JLabel(ColorHelper.color2html((Color)o)); 117 l.setBackground((Color)o); 118 l.setOpaque(true); 119 return l; 120 } 121 return oldColorsRenderer.getTableCellRendererComponent(t,tr(o.toString()),selected,focus,row,column); 122 } 123 }); 124 colors.getColumnModel().getColumn(1).setWidth(100); 45 /** 46 * Set the colors to be shown in the preference table. This method creates a table model if 47 * none exists and overwrites all existing values. 48 * @param colorMap the map holding the colors 49 * (key = color id (without prefixes, so only <code>background</code>; not <code>color.background</code>), 50 * value = html representation of the color. 51 */ 52 public void setColorModel(Map<String, String> colorMap) { 53 if(tableModel == null) { 54 tableModel = new DefaultTableModel(); 55 tableModel.addColumn(tr("Color")); 56 tableModel.addColumn(tr("Name")); 57 } 125 58 126 JButton colorEdit = new JButton(tr("Choose")); 127 colorEdit.addActionListener(new ActionListener(){ 128 public void actionPerformed(ActionEvent e) { 129 if (colors.getSelectedRowCount() == 0) { 130 JOptionPane.showMessageDialog(gui, tr("Please select a color.")); 131 return; 132 } 133 int sel = colors.getSelectedRow(); 134 JColorChooser chooser = new JColorChooser((Color)colors.getValueAt(sel, 1)); 135 int answer = JOptionPane.showConfirmDialog(gui, chooser, tr("Choose a color for {0}", colors.getValueAt(sel, 0)), JOptionPane.OK_CANCEL_OPTION); 136 if (answer == JOptionPane.OK_OPTION) 137 colors.setValueAt(chooser.getColor(), sel, 1); 138 } 139 }); 140 colors.setToolTipText(tr("Colors used by different objects in JOSM.")); 141 colors.setPreferredScrollableViewportSize(new Dimension(100,112)); 142 143 JPanel panel = new JPanel(new GridBagLayout()); 144 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 145 JScrollPane scrollpane = new JScrollPane(colors); 146 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 147 panel.add(scrollpane, GBC.eol().fill(GBC.BOTH)); 148 panel.add(colorEdit, GBC.eol().anchor(GBC.EAST)); 149 gui.displaycontent.addTab(tr("Colors"), panel); 59 // clear old model: 60 while(tableModel.getRowCount() > 0) { 61 tableModel.removeRow(0); 62 } 63 // fill model with colors: 64 List<String> colorKeyList = new ArrayList<String>(); 65 for(String key : colorMap.keySet()) { 66 colorKeyList.add(key); 67 } 68 Collections.sort(colorKeyList); 69 for (String key : colorKeyList) { 70 Vector<Object> row = new Vector<Object>(2); 71 row.add(key); 72 row.add(ColorHelper.html2color(colorMap.get(key))); 73 tableModel.addRow(row); 74 } 75 if(this.colors != null) { 76 this.colors.repaint(); 77 } 150 78 } 151 79 152 /** 153 * Add all missing color entries. 154 */ 155 private void fixColorPrefixes(Map<String, String> prefColorMap) { 156 String[] cp = { 157 marktr("background"), ColorHelper.color2html(Color.black), 158 marktr("node"), ColorHelper.color2html(Color.red), 159 marktr("way"), ColorHelper.color2html(SimplePaintVisitor.darkblue), 160 marktr("incomplete way"), ColorHelper.color2html(SimplePaintVisitor.darkerblue), 161 marktr("relation"), ColorHelper.color2html(SimplePaintVisitor.teal), 162 marktr("selected"), ColorHelper.color2html(Color.white), 163 marktr("gps marker"), ColorHelper.color2html(Color.gray), 164 marktr("gps point"), ColorHelper.color2html(Color.gray), 165 marktr("conflict"), ColorHelper.color2html(Color.gray), 166 marktr("scale"), ColorHelper.color2html(Color.white), 167 marktr("inactive"), ColorHelper.color2html(Color.darkGray), 168 }; 169 for (int i = 0; i < cp.length/2; ++i) 170 { 171 if (!Main.pref.hasKey("color."+cp[i*2])) 172 Main.pref.put("color."+cp[i*2], cp[i*2+1]); 173 Main.pref.putDefault("color."+cp[i*2], cp[i*2+1]); 174 } 80 /** 81 * Returns a map with the colors in the table (key = color name without prefix, value = html color code). 82 * @return a map holding the colors. 83 */ 84 public Map<String, String> getColorModel() { 85 String key; 86 String value; 87 Map<String, String> colorMap = new HashMap<String, String>(); 88 for(int row = 0; row < tableModel.getRowCount(); ++row) { 89 key = (String)tableModel.getValueAt(row, 0); 90 value = ColorHelper.color2html((Color)tableModel.getValueAt(row, 1)); 91 colorMap.put(key, value); 92 } 93 return colorMap; 175 94 } 176 95 177 public void ok() { 178 for (int i = 0; i < colors.getRowCount(); ++i) { 179 String name = (String)colors.getValueAt(i, 0); 180 Color col = (Color)colors.getValueAt(i, 1); 181 Main.pref.put("color." + name, ColorHelper.color2html(col)); 182 } 96 public void addGui(final PreferenceDialog gui) { 97 // initial fill with colors from preferences: 98 Map<String,String> prefColorMap = new TreeMap<String, String>(Main.pref.getAllPrefix("color.")); 99 fixColorPrefixes(prefColorMap); 100 Map<String,String> colorMap = new TreeMap<String, String>(); 101 for(String key : prefColorMap.keySet()) { 102 colorMap.put(key.substring("color.".length()), prefColorMap.get(key)); 103 } 104 setColorModel(colorMap); 105 106 colors = new JTable(tableModel) { 107 @Override public boolean isCellEditable(int row, int column) { 108 return false; 109 } 110 }; 111 colors.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 112 final TableCellRenderer oldColorsRenderer = colors.getDefaultRenderer(Object.class); 113 colors.setDefaultRenderer(Object.class, new TableCellRenderer(){ 114 public Component getTableCellRendererComponent(JTable t, Object o, boolean selected, boolean focus, int row, int column) { 115 if (column == 1) { 116 JLabel l = new JLabel(ColorHelper.color2html((Color)o)); 117 l.setBackground((Color)o); 118 l.setOpaque(true); 119 return l; 120 } 121 return oldColorsRenderer.getTableCellRendererComponent(t,tr(o.toString()),selected,focus,row,column); 122 } 123 }); 124 colors.getColumnModel().getColumn(1).setWidth(100); 125 126 JButton colorEdit = new JButton(tr("Choose")); 127 colorEdit.addActionListener(new ActionListener(){ 128 public void actionPerformed(ActionEvent e) { 129 if (colors.getSelectedRowCount() == 0) { 130 JOptionPane.showMessageDialog(gui, tr("Please select a color.")); 131 return; 132 } 133 int sel = colors.getSelectedRow(); 134 JColorChooser chooser = new JColorChooser((Color)colors.getValueAt(sel, 1)); 135 int answer = JOptionPane.showConfirmDialog(gui, chooser, tr("Choose a color for {0}", colors.getValueAt(sel, 0)), JOptionPane.OK_CANCEL_OPTION); 136 if (answer == JOptionPane.OK_OPTION) 137 colors.setValueAt(chooser.getColor(), sel, 1); 138 } 139 }); 140 colors.setToolTipText(tr("Colors used by different objects in JOSM.")); 141 colors.setPreferredScrollableViewportSize(new Dimension(100,112)); 142 143 JPanel panel = new JPanel(new GridBagLayout()); 144 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 145 JScrollPane scrollpane = new JScrollPane(colors); 146 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 147 panel.add(scrollpane, GBC.eol().fill(GBC.BOTH)); 148 panel.add(colorEdit, GBC.eol().anchor(GBC.EAST)); 149 gui.displaycontent.addTab(tr("Colors"), panel); 150 } 151 152 /** 153 * Add all missing color entries. 154 */ 155 private void fixColorPrefixes(Map<String, String> prefColorMap) { 156 String[] cp = { 157 marktr("background"), ColorHelper.color2html(Color.black), 158 marktr("node"), ColorHelper.color2html(Color.red), 159 marktr("way"), ColorHelper.color2html(SimplePaintVisitor.darkblue), 160 marktr("incomplete way"), ColorHelper.color2html(SimplePaintVisitor.darkerblue), 161 marktr("relation"), ColorHelper.color2html(SimplePaintVisitor.teal), 162 marktr("selected"), ColorHelper.color2html(Color.white), 163 marktr("gps marker"), ColorHelper.color2html(Color.gray), 164 marktr("gps point"), ColorHelper.color2html(Color.gray), 165 marktr("conflict"), ColorHelper.color2html(Color.gray), 166 marktr("scale"), ColorHelper.color2html(Color.white), 167 marktr("inactive"), ColorHelper.color2html(Color.darkGray), 168 }; 169 for (int i = 0; i < cp.length/2; ++i) 170 { 171 if (!Main.pref.hasKey("color."+cp[i*2])) 172 Main.pref.put("color."+cp[i*2], cp[i*2+1]); 173 Main.pref.putDefault("color."+cp[i*2], cp[i*2+1]); 174 } 175 } 176 177 public void ok() { 178 for (int i = 0; i < colors.getRowCount(); ++i) { 179 String name = (String)colors.getValueAt(i, 0); 180 Color col = (Color)colors.getValueAt(i, 1); 181 Main.pref.put("color." + name, ColorHelper.color2html(col)); 182 } 183 183 org.openstreetmap.josm.gui.layer.OsmDataLayer.createHatchTexture(); 184 184 } -
trunk/src/org/openstreetmap/josm/gui/preferences/DrawingPreference.java
r1165 r1169 22 22 public class DrawingPreference implements PreferenceSetting { 23 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 24 private JCheckBox drawRawGpsLines = new JCheckBox(tr("Draw lines between raw gps points.")); 25 private JTextField drawRawGpsMaxLineLength = new JTextField(8); 26 private JCheckBox forceRawGpsLines = new JCheckBox(tr("Force lines if no segments imported.")); 27 private JCheckBox largeGpsPoints = new JCheckBox(tr("Draw large GPS points.")); 28 private JCheckBox colorTracks = new JCheckBox(tr("Color tracks by velocity.")); 29 private JCheckBox directionHint = new JCheckBox(tr("Draw Direction Arrows")); 30 private JCheckBox drawGpsArrows = new JCheckBox(tr("Draw Direction Arrows")); 31 private JCheckBox drawGpsArrowsFast = new JCheckBox(tr("Fast drawing (looks uglier)")); 32 private JTextField drawGpsArrowsMinDist = new JTextField(8); 33 private JCheckBox interestingDirections = new JCheckBox(tr("Only interesting direction hints (e.g. with oneway tag).")); 34 private JCheckBox segmentOrderNumber = new JCheckBox(tr("Draw segment order numbers")); 35 private JCheckBox sourceBounds = new JCheckBox(tr("Draw boundaries of downloaded data")); 36 private JCheckBox virtualNodes = new JCheckBox(tr("Draw virtual nodes in select mode")); 37 private JCheckBox inactive = new JCheckBox(tr("Draw inactive layers in other color")); 38 private JCheckBox useAntialiasing = new JCheckBox(tr("Smooth map graphics (antialiasing)")); 39 39 40 41 40 public void addGui(PreferenceDialog gui) { 41 gui.display.setPreferredSize(new Dimension(400,600)); 42 42 JPanel panel = new JPanel(new GridBagLayout()); 43 43 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 44 44 45 46 47 45 // drawRawGpsLines 46 drawRawGpsLines.addActionListener(new ActionListener(){ 47 public void actionPerformed(ActionEvent e) { 48 48 forceRawGpsLines.setEnabled(drawRawGpsLines.isSelected()); 49 49 drawRawGpsMaxLineLength.setEnabled(drawRawGpsLines.isSelected()); … … 52 52 drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 53 53 colorTracks.setEnabled(drawRawGpsLines.isSelected()); 54 55 56 57 58 54 } 55 }); 56 drawRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines")); 57 drawRawGpsLines.setToolTipText(tr("If your gps device draw too few lines, select this to draw lines along your way.")); 58 panel.add(drawRawGpsLines, GBC.eol().insets(20,0,0,0)); 59 59 60 61 62 63 64 65 60 // drawRawGpsMaxLineLength 61 drawRawGpsMaxLineLength.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.max-line-length", -1))); 62 drawRawGpsMaxLineLength.setToolTipText(tr("Maximum length (in meters) to draw lines. Set to '-1' to draw all lines.")); 63 drawRawGpsMaxLineLength.setEnabled(drawRawGpsLines.isSelected()); 64 panel.add(new JLabel(tr("Maximum length (meters)")), GBC.std().insets(40,0,0,0)); 65 panel.add(drawRawGpsMaxLineLength, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 66 66 67 68 69 70 71 72 73 74 75 67 // forceRawGpsLines 68 forceRawGpsLines.setToolTipText(tr("Force drawing of lines if the imported data contain no line information.")); 69 forceRawGpsLines.setSelected(Main.pref.getBoolean("draw.rawgps.lines.force")); 70 forceRawGpsLines.setEnabled(drawRawGpsLines.isSelected()); 71 panel.add(forceRawGpsLines, GBC.eop().insets(40,0,0,0)); 72 73 // drawGpsArrows 74 drawGpsArrows.addActionListener(new ActionListener(){ 75 public void actionPerformed(ActionEvent e) { 76 76 drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 77 77 drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 78 79 80 81 82 83 78 } 79 }); 80 drawGpsArrows.setToolTipText(tr("Draw direction arrows for lines, connecting GPS points.")); 81 drawGpsArrows.setSelected(Main.pref.getBoolean("draw.rawgps.direction")); 82 drawGpsArrows.setEnabled(drawRawGpsLines.isSelected()); 83 panel.add(drawGpsArrows, GBC.eop().insets(40,0,0,0)); 84 84 85 86 87 88 89 85 // drawGpsArrowsFast 86 drawGpsArrowsFast.setToolTipText(tr("Draw the direction arrows using table lookups instead of complex math.")); 87 drawGpsArrowsFast.setSelected(Main.pref.getBoolean("draw.rawgps.alternatedirection")); 88 drawGpsArrowsFast.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 89 panel.add(drawGpsArrowsFast, GBC.eop().insets(60,0,0,0)); 90 90 91 92 93 94 95 96 91 // drawGpsArrowsMinDist 92 drawGpsArrowsMinDist.setToolTipText(tr("Don't draw arrows if they are not at least this distance away from the last one.")); 93 drawGpsArrowsMinDist.setText(Integer.toString(Main.pref.getInteger("draw.rawgps.min-arrow-distance", 0))); 94 drawGpsArrowsMinDist.setEnabled(drawGpsArrows.isSelected() && drawGpsArrows.isEnabled()); 95 panel.add(new JLabel(tr("Minimum distance (pixels)")), GBC.std().insets(60,0,0,0)); 96 panel.add(drawGpsArrowsMinDist, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 97 97 98 // colorTracks 99 colorTracks.setSelected(Main.pref.getBoolean("draw.rawgps.colors")); 100 colorTracks.setToolTipText(tr("Choose the hue for the track color by the velocity at that point.")); 101 colorTracks.setEnabled(drawRawGpsLines.isSelected()); 102 panel.add(colorTracks, GBC.eop().insets(40,0,0,0)); 103 104 // largeGpsPoints 105 largeGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.large")); 106 largeGpsPoints.setToolTipText(tr("Draw larger dots for the GPS points.")); 107 panel.add(largeGpsPoints, GBC.eop().insets(20,0,0,0)); 108 109 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 110 JScrollPane scrollpane = new JScrollPane(panel); 111 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 112 gui.displaycontent.addTab(tr("GPS Points"), scrollpane); 113 panel = new JPanel(new GridBagLayout()); 114 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 98 // colorTracks 99 colorTracks.setSelected(Main.pref.getBoolean("draw.rawgps.colors")); 100 colorTracks.setToolTipText(tr("Choose the hue for the track color by the velocity at that point.")); 101 colorTracks.setEnabled(drawRawGpsLines.isSelected()); 102 panel.add(colorTracks, GBC.eop().insets(40,0,0,0)); 115 103 116 // directionHint 117 directionHint.addActionListener(new ActionListener(){ 118 public void actionPerformed(ActionEvent e) { 104 // largeGpsPoints 105 largeGpsPoints.setSelected(Main.pref.getBoolean("draw.rawgps.large")); 106 largeGpsPoints.setToolTipText(tr("Draw larger dots for the GPS points.")); 107 panel.add(largeGpsPoints, GBC.eop().insets(20,0,0,0)); 108 109 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 110 JScrollPane scrollpane = new JScrollPane(panel); 111 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 112 gui.displaycontent.addTab(tr("GPS Points"), scrollpane); 113 panel = new JPanel(new GridBagLayout()); 114 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 115 116 // directionHint 117 directionHint.addActionListener(new ActionListener(){ 118 public void actionPerformed(ActionEvent e) { 119 119 if (directionHint.isSelected()){ 120 120 interestingDirections.setSelected(Main.pref.getBoolean("draw.segment.relevant_directions_only")); … … 123 123 } 124 124 interestingDirections.setEnabled(directionHint.isSelected()); 125 126 127 128 129 125 } 126 }); 127 directionHint.setToolTipText(tr("Draw direction hints for way segments.")); 128 directionHint.setSelected(Main.pref.getBoolean("draw.segment.direction")); 129 panel.add(directionHint, GBC.eop().insets(20,0,0,0)); 130 130 131 // only interesting directions 132 interestingDirections.setToolTipText(tr("Only interesting direction hints (e.g. with oneway tag).")); 133 interestingDirections.setSelected(Main.pref.getBoolean("draw.segment.relevant_directions_only")); 134 interestingDirections.setEnabled(directionHint.isSelected()); 135 panel.add(interestingDirections, GBC.eop().insets(40,0,0,0)); 136 137 // segment order number 138 segmentOrderNumber.setToolTipText(tr("Draw the order numbers of all segments within their way.")); 139 segmentOrderNumber.setSelected(Main.pref.getBoolean("draw.segment.order_number")); 140 panel.add(segmentOrderNumber, GBC.eop().insets(20,0,0,0)); 141 142 // antialiasing 143 useAntialiasing.setToolTipText(tr("Apply antialiasing to the map view resulting in a smoother appearance.")); 144 useAntialiasing.setSelected(Main.pref.getBoolean("mappaint.use-antialiasing")); 145 panel.add(useAntialiasing, GBC.eop().insets(20,0,0,0)); 146 147 // downloaded area 148 sourceBounds.setToolTipText(tr("Draw the boundaries of data loaded from the server.")); 149 sourceBounds.setSelected(Main.pref.getBoolean("draw.data.downloaded_area", true)); 150 panel.add(sourceBounds, GBC.eop().insets(20,0,0,0)); 131 // only interesting directions 132 interestingDirections.setToolTipText(tr("Only interesting direction hints (e.g. with oneway tag).")); 133 interestingDirections.setSelected(Main.pref.getBoolean("draw.segment.relevant_directions_only")); 134 interestingDirections.setEnabled(directionHint.isSelected()); 135 panel.add(interestingDirections, GBC.eop().insets(40,0,0,0)); 151 136 152 // virtual nodes 153 virtualNodes.setToolTipText(tr("Draw virtual nodes in select mode for easy way modification."));154 virtualNodes.setSelected(Main.pref.getInteger("mappaint.node.virtual-size", 8) != 0);155 panel.add(virtualNodes, GBC.eop().insets(20,0,0,0));137 // segment order number 138 segmentOrderNumber.setToolTipText(tr("Draw the order numbers of all segments within their way.")); 139 segmentOrderNumber.setSelected(Main.pref.getBoolean("draw.segment.order_number")); 140 panel.add(segmentOrderNumber, GBC.eop().insets(20,0,0,0)); 156 141 157 // background layers in inactive color 158 inactive.setToolTipText(tr("Draw the inactive data layers in a different color."));159 inactive.setSelected(Main.pref.getBoolean("draw.data.inactive_color", true));160 panel.add(inactive, GBC.eop().insets(20,0,0,0));142 // antialiasing 143 useAntialiasing.setToolTipText(tr("Apply antialiasing to the map view resulting in a smoother appearance.")); 144 useAntialiasing.setSelected(Main.pref.getBoolean("mappaint.use-antialiasing")); 145 panel.add(useAntialiasing, GBC.eop().insets(20,0,0,0)); 161 146 162 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 163 scrollpane = new JScrollPane(panel); 164 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 165 gui.displaycontent.addTab(tr("OSM Data"), scrollpane); 166 } 147 // downloaded area 148 sourceBounds.setToolTipText(tr("Draw the boundaries of data loaded from the server.")); 149 sourceBounds.setSelected(Main.pref.getBoolean("draw.data.downloaded_area", true)); 150 panel.add(sourceBounds, GBC.eop().insets(20,0,0,0)); 167 151 168 public void ok() { 169 Main.pref.put("draw.rawgps.lines", drawRawGpsLines.isSelected()); 170 Main.pref.put("draw.rawgps.max-line-length", drawRawGpsMaxLineLength.getText()); 171 Main.pref.put("draw.rawgps.lines.force", forceRawGpsLines.isSelected()); 172 Main.pref.put("draw.rawgps.direction", drawGpsArrows.isSelected()); 173 Main.pref.put("draw.rawgps.alternatedirection", drawGpsArrowsFast.isSelected()); 174 Main.pref.put("draw.rawgps.min-arrow-distance", drawGpsArrowsMinDist.getText()); 175 Main.pref.put("draw.rawgps.colors", colorTracks.isSelected()); 176 Main.pref.put("draw.rawgps.large", largeGpsPoints.isSelected()); 177 Main.pref.put("draw.segment.direction", directionHint.isSelected()); 178 Main.pref.put("draw.segment.relevant_directions_only", interestingDirections.isSelected()); 179 Main.pref.put("draw.segment.order_number", segmentOrderNumber.isSelected()); 180 Main.pref.put("draw.data.downloaded_area", sourceBounds.isSelected()); 181 Main.pref.put("draw.data.inactive_color", inactive.isSelected()); 182 Main.pref.put("mappaint.use-antialiasing", useAntialiasing.isSelected()); 183 int vn = Main.pref.getInteger("mappaint.node.virtual-size", 8); 184 if(virtualNodes.isSelected()) { if (vn < 1) vn = 8; } 185 else { vn = 0; } 186 Main.pref.put("mappaint.node.virtual-size", Integer.toString(vn)); 187 } 152 // virtual nodes 153 virtualNodes.setToolTipText(tr("Draw virtual nodes in select mode for easy way modification.")); 154 virtualNodes.setSelected(Main.pref.getInteger("mappaint.node.virtual-size", 8) != 0); 155 panel.add(virtualNodes, GBC.eop().insets(20,0,0,0)); 156 157 // background layers in inactive color 158 inactive.setToolTipText(tr("Draw the inactive data layers in a different color.")); 159 inactive.setSelected(Main.pref.getBoolean("draw.data.inactive_color", true)); 160 panel.add(inactive, GBC.eop().insets(20,0,0,0)); 161 162 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 163 scrollpane = new JScrollPane(panel); 164 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 165 gui.displaycontent.addTab(tr("OSM Data"), scrollpane); 166 } 167 168 public void ok() { 169 Main.pref.put("draw.rawgps.lines", drawRawGpsLines.isSelected()); 170 Main.pref.put("draw.rawgps.max-line-length", drawRawGpsMaxLineLength.getText()); 171 Main.pref.put("draw.rawgps.lines.force", forceRawGpsLines.isSelected()); 172 Main.pref.put("draw.rawgps.direction", drawGpsArrows.isSelected()); 173 Main.pref.put("draw.rawgps.alternatedirection", drawGpsArrowsFast.isSelected()); 174 Main.pref.put("draw.rawgps.min-arrow-distance", drawGpsArrowsMinDist.getText()); 175 Main.pref.put("draw.rawgps.colors", colorTracks.isSelected()); 176 Main.pref.put("draw.rawgps.large", largeGpsPoints.isSelected()); 177 Main.pref.put("draw.segment.direction", directionHint.isSelected()); 178 Main.pref.put("draw.segment.relevant_directions_only", interestingDirections.isSelected()); 179 Main.pref.put("draw.segment.order_number", segmentOrderNumber.isSelected()); 180 Main.pref.put("draw.data.downloaded_area", sourceBounds.isSelected()); 181 Main.pref.put("draw.data.inactive_color", inactive.isSelected()); 182 Main.pref.put("mappaint.use-antialiasing", useAntialiasing.isSelected()); 183 int vn = Main.pref.getInteger("mappaint.node.virtual-size", 8); 184 if(virtualNodes.isSelected()) { if (vn < 1) vn = 8; } 185 else { vn = 0; } 186 Main.pref.put("mappaint.node.virtual-size", Integer.toString(vn)); 187 } 188 188 } -
trunk/src/org/openstreetmap/josm/gui/preferences/FilePreferences.java
r999 r1169 14 14 * Out of pure laziness, I add the file stuff to connection tab. 15 15 * Feel free to fix this. 16 * 16 * 17 17 * @author imi 18 18 */ 19 19 public class FilePreferences implements PreferenceSetting { 20 20 21 22 23 24 25 26 27 21 private JCheckBox keepBackup = new JCheckBox(tr("Keep backup files")); 22 23 public void addGui(PreferenceDialog gui) { 24 gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL)); 25 keepBackup.setSelected(Main.pref.getBoolean("save.keepbackup")); 26 keepBackup.setToolTipText(tr("When saving, keep backup files ending with a ~")); 27 gui.connection.add(keepBackup, GBC.eol().insets(20,0,0,0)); 28 28 } 29 29 30 31 30 public void ok() { 31 Main.pref.put("save.keepbackup", keepBackup.isSelected()); 32 32 } 33 33 } -
trunk/src/org/openstreetmap/josm/gui/preferences/LafPreference.java
r1165 r1169 25 25 public class LafPreference implements PreferenceSetting { 26 26 27 28 29 30 31 32 33 27 /** 28 * ComboBox with all look and feels. 29 */ 30 private JComboBox lafCombo; 31 public JPanel panel = new JPanel(new GridBagLayout()); 32 private JCheckBox showSplashScreen = new JCheckBox(tr("Show splash screen at startup")); 33 private JCheckBox showID = new JCheckBox(tr("Show object ID in selection lists")); 34 34 35 36 35 public void addGui(PreferenceDialog gui) { 36 lafCombo = new JComboBox(UIManager.getInstalledLookAndFeels()); 37 37 38 39 40 41 42 43 44 45 46 47 48 49 38 // let's try to load additional LookAndFeels and put them into the list 39 try { 40 Class<?> Cquaqua = Class.forName("ch.randelshofer.quaqua.QuaquaLookAndFeel"); 41 Object Oquaqua = Cquaqua.getConstructor((Class[])null).newInstance((Object[])null); 42 // no exception? Then Go! 43 lafCombo.addItem( 44 new UIManager.LookAndFeelInfo(((javax.swing.LookAndFeel)Oquaqua).getName(), "ch.randelshofer.quaqua.QuaquaLookAndFeel") 45 ); 46 } catch (Exception ex) { 47 // just ignore, Quaqua may not even be installed... 48 //System.out.println("Failed to load Quaqua: " + ex); 49 } 50 50 51 52 53 54 55 56 57 51 String laf = Main.pref.get("laf"); 52 for (int i = 0; i < lafCombo.getItemCount(); ++i) { 53 if (((LookAndFeelInfo)lafCombo.getItemAt(i)).getClassName().equals(laf)) { 54 lafCombo.setSelectedIndex(i); 55 break; 56 } 57 } 58 58 59 60 61 62 63 64 65 59 final ListCellRenderer oldRenderer = lafCombo.getRenderer(); 60 lafCombo.setRenderer(new DefaultListCellRenderer(){ 61 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 62 return oldRenderer.getListCellRendererComponent(list, ((LookAndFeelInfo)value).getName(), index, isSelected, cellHasFocus); 63 } 64 }); 65 lafCombo.addActionListener(gui.requireRestartAction); 66 66 67 67 panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 68 68 69 70 71 72 69 // Show splash screen on startup 70 showSplashScreen.setToolTipText(tr("Show splash screen at startup")); 71 showSplashScreen.setSelected(Main.pref.getBoolean("draw.splashscreen", true)); 72 panel.add(showSplashScreen, GBC.eop().insets(20, 0, 0, 0)); 73 73 74 75 76 77 74 // Show ID in selection 75 showID.setToolTipText(tr("Show object ID in selection lists")); 76 showID.setSelected(Main.pref.getBoolean("osm-primitives.showid", false)); 77 panel.add(showID, GBC.eop().insets(20, 0, 0, 0)); 78 78 79 79 panel.add(Box.createVerticalGlue(), GBC.eol().insets(0, 20, 0, 0)); 80 80 81 82 83 81 panel.add(new JLabel(tr("Look and Feel")), GBC.std().insets(20, 0, 0, 0)); 82 panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 83 panel.add(lafCombo, GBC.eol().fill(GBC.HORIZONTAL)); 84 84 85 86 87 88 85 JScrollPane scrollpane = new JScrollPane(panel); 86 scrollpane.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 87 gui.displaycontent.addTab(tr("Look and Feel"), scrollpane); 88 } 89 89 90 91 92 93 90 public void ok() { 91 Main.pref.put("laf", ((LookAndFeelInfo)lafCombo.getSelectedItem()).getClassName()); 92 Main.pref.put("draw.splashscreen", showSplashScreen.isSelected()); 93 Main.pref.put("osm-primitives.showid", showID.isSelected()); 94 94 } 95 95 -
trunk/src/org/openstreetmap/josm/gui/preferences/LanguagePreference.java
r1165 r1169 20 20 21 21 public class LanguagePreference implements PreferenceSetting { 22 23 24 25 26 22 /** 23 * ComboBox with all available Translations 24 */ 25 private JComboBox langCombo; 26 private final Locale AUTO_LANGUAGE = null; 27 27 28 public void addGui(PreferenceDialog gui) { 29 langCombo = new JComboBox(I18n.getAvailableTranslations()); 30 langCombo.insertItemAt(AUTO_LANGUAGE, 0); // Default 31 langCombo.insertItemAt(Locale.ENGLISH, 1); // Built-in language 32 String ln = Main.pref.get("language"); 33 langCombo.setSelectedIndex(0); 34 35 if (ln != null) { 36 for (int i = 1; i < langCombo.getItemCount(); ++i) { 37 if (((Locale) langCombo.getItemAt(i)).toString().equals(ln)) { 38 langCombo.setSelectedIndex(i); 39 break; 40 } 41 } 42 } 28 public void addGui(PreferenceDialog gui) { 29 langCombo = new JComboBox(I18n.getAvailableTranslations()); 30 langCombo.insertItemAt(AUTO_LANGUAGE, 0); // Default 31 langCombo.insertItemAt(Locale.ENGLISH, 1); // Built-in language 32 String ln = Main.pref.get("language"); 33 langCombo.setSelectedIndex(0); 43 34 44 final ListCellRenderer oldRenderer = langCombo.getRenderer(); 45 langCombo.setRenderer(new DefaultListCellRenderer() { 46 @Override 47 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, 48 boolean cellHasFocus) { 49 Locale l = (Locale) value; 50 return oldRenderer.getListCellRendererComponent(list, 51 l == AUTO_LANGUAGE ? tr("Default (Auto determined)") : l.getDisplayName(), 52 index, isSelected, cellHasFocus); 53 } 54 }); 55 langCombo.addActionListener(gui.requireRestartAction); 35 if (ln != null) { 36 for (int i = 1; i < langCombo.getItemCount(); ++i) { 37 if (((Locale) langCombo.getItemAt(i)).toString().equals(ln)) { 38 langCombo.setSelectedIndex(i); 39 break; 40 } 41 } 42 } 56 43 57 JPanel panel = null; 58 for(PreferenceSetting s : gui.settings) 59 { 60 if(s instanceof LafPreference) 61 panel = ((LafPreference)s).panel; 62 } 63 panel.add(new JLabel(tr("Language")), GBC.std().insets(20, 0, 0, 0)); 64 panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 65 panel.add(langCombo, GBC.eol().fill(GBC.HORIZONTAL)); 66 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 67 } 44 final ListCellRenderer oldRenderer = langCombo.getRenderer(); 45 langCombo.setRenderer(new DefaultListCellRenderer() { 46 @Override 47 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, 48 boolean cellHasFocus) { 49 Locale l = (Locale) value; 50 return oldRenderer.getListCellRendererComponent(list, 51 l == AUTO_LANGUAGE ? tr("Default (Auto determined)") : l.getDisplayName(), 52 index, isSelected, cellHasFocus); 53 } 54 }); 55 langCombo.addActionListener(gui.requireRestartAction); 68 56 69 public void ok() { 70 if(langCombo.getSelectedItem() == null) 71 { 72 Main.pref.put("language", null); 73 } 74 else 75 { 76 String l = ((Locale)langCombo.getSelectedItem()).toString(); 77 Main.pref.put("language", l); 78 } 79 } 57 JPanel panel = null; 58 for(PreferenceSetting s : gui.settings) 59 { 60 if(s instanceof LafPreference) 61 panel = ((LafPreference)s).panel; 62 } 63 panel.add(new JLabel(tr("Language")), GBC.std().insets(20, 0, 0, 0)); 64 panel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 65 panel.add(langCombo, GBC.eol().fill(GBC.HORIZONTAL)); 66 panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH)); 67 } 68 69 public void ok() { 70 if(langCombo.getSelectedItem() == null) 71 { 72 Main.pref.put("language", null); 73 } 74 else 75 { 76 String l = ((Locale)langCombo.getSelectedItem()).toString(); 77 Main.pref.put("language", l); 78 } 79 } 80 80 } -
trunk/src/org/openstreetmap/josm/gui/preferences/MapPaintPreference.java
r627 r1169 5 5 6 6 public class MapPaintPreference implements PreferenceSetting { 7 8 public void addGui(final PreferenceDialog gui) {9 // this is intended for a future configuration panel for mappaint!10 }11 7 12 public void ok() {13 // dummy 14 8 public void addGui(final PreferenceDialog gui) { 9 // this is intended for a future configuration panel for mappaint! 10 } 15 11 16 /** 17 * Initialize the styles 18 */ 19 public static void initialize() { 20 MapPaintStyles.readFromPreferences(); 21 } 12 public void ok() { 13 // dummy 14 } 15 16 /** 17 * Initialize the styles 18 */ 19 public static void initialize() { 20 MapPaintStyles.readFromPreferences(); 21 } 22 22 } -
trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java
r1133 r1169 49 49 public class PluginPreference implements PreferenceSetting { 50 50 51 52 53 54 55 * 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 } 164 165 166 167 168 169 170 171 172 173 } 174 175 176 177 178 179 180 181 182 } 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 51 /** 52 * Only the plugin name, its jar location and the description. 53 * In other words, this is the minimal requirement the plugin preference page 54 * needs to show the plugin as available 55 * 56 * @author imi 57 */ 58 public static class PluginDescription implements Comparable<Object> { 59 // Note: All the following need to be public instance variables of 60 // type String. (Plugin description XMLs from the server are parsed 61 // with tools.XmlObjectParser, which uses reflection to access them.) 62 public String name; 63 public String description; 64 public String resource; 65 public String version; 66 public PluginDescription(String name, String description, String resource, String version) { 67 this.name = name; 68 this.description = description; 69 this.resource = resource; 70 this.version = version; 71 } 72 public PluginDescription() { 73 } 74 public int compareTo(Object n) { 75 if(n instanceof PluginDescription) 76 return name.compareToIgnoreCase(((PluginDescription)n).name); 77 return -1; 78 } 79 } 80 81 private Map<PluginDescription, Boolean> pluginMap; 82 private JPanel plugin; 83 private class MyBox extends Box { 84 int lastwidth; 85 int offset = 40; 86 public MyBox() 87 { 88 super(BoxLayout.Y_AXIS); 89 } 90 public int myGetWidth() 91 { 92 int w = plugin.getWidth()-offset; 93 if(w <= 0) w = 450; 94 lastwidth = w; 95 return w; 96 } 97 public void paint(Graphics g) 98 { 99 if(lastwidth != plugin.getWidth()-offset) 100 refreshPluginPanel(gui); 101 super.paint(g); 102 } 103 } 104 private MyBox pluginPanel = new MyBox(); 105 private PreferenceDialog gui; 106 107 public void addGui(final PreferenceDialog gui) { 108 this.gui = gui; 109 plugin = gui.createPreferenceTab("plugin", tr("Plugins"), tr("Configure available plugins."), false); 110 JScrollPane pluginPane = new JScrollPane(pluginPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 111 pluginPane.setBorder(null); 112 plugin.add(pluginPane, GBC.eol().fill(GBC.BOTH)); 113 plugin.add(GBC.glue(0,10), GBC.eol()); 114 JButton morePlugins = new JButton(tr("Download List")); 115 morePlugins.addActionListener(new ActionListener(){ 116 public void actionPerformed(ActionEvent e) { 117 int count = PluginDownloader.downloadDescription(); 118 if (count > 0) 119 JOptionPane.showMessageDialog(Main.parent, 120 trn("Downloaded plugin information from {0} site", 121 "Downloaded plugin information from {0} sites", count, count)); 122 else 123 JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found.")); 124 refreshPluginPanel(gui); 125 } 126 }); 127 plugin.add(morePlugins, GBC.std().insets(0,0,10,0)); 128 129 JButton update = new JButton(tr("Update")); 130 update.addActionListener(new ActionListener(){ 131 public void actionPerformed(ActionEvent e) { 132 update(); 133 refreshPluginPanel(gui); 134 } 135 }); 136 plugin.add(update, GBC.std().insets(0,0,10,0)); 137 138 JButton configureSites = new JButton(tr("Configure Sites ...")); 139 configureSites.addActionListener(new ActionListener(){ 140 public void actionPerformed(ActionEvent e) { 141 configureSites(); 142 } 143 }); 144 plugin.add(configureSites, GBC.std()); 145 146 refreshPluginPanel(gui); 147 } 148 149 private void configureSites() { 150 JPanel p = new JPanel(new GridBagLayout()); 151 p.add(new JLabel(tr("Add either site-josm.xml or Wiki Pages.")), GBC.eol()); 152 final DefaultListModel model = new DefaultListModel(); 153 for (String s : PluginDownloader.getSites()) 154 model.addElement(s); 155 final JList list = new JList(model); 156 p.add(new JScrollPane(list), GBC.std().fill()); 157 JPanel buttons = new JPanel(new GridBagLayout()); 158 buttons.add(new JButton(new AbstractAction(tr("Add")){ 159 public void actionPerformed(ActionEvent e) { 160 String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages.")); 161 if (s != null) 162 model.addElement(s); 163 } 164 }), GBC.eol().fill(GBC.HORIZONTAL)); 165 buttons.add(new JButton(new AbstractAction(tr("Edit")){ 166 public void actionPerformed(ActionEvent e) { 167 if (list.getSelectedValue() == null) { 168 JOptionPane.showMessageDialog(gui, tr("Please select an entry.")); 169 return; 170 } 171 String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."), list.getSelectedValue()); 172 model.setElementAt(s, list.getSelectedIndex()); 173 } 174 }), GBC.eol().fill(GBC.HORIZONTAL)); 175 buttons.add(new JButton(new AbstractAction(tr("Delete")){ 176 public void actionPerformed(ActionEvent event) { 177 if (list.getSelectedValue() == null) { 178 JOptionPane.showMessageDialog(gui, tr("Please select an entry.")); 179 return; 180 } 181 model.removeElement(list.getSelectedValue()); 182 } 183 }), GBC.eol().fill(GBC.HORIZONTAL)); 184 p.add(buttons, GBC.eol()); 185 int answer = JOptionPane.showConfirmDialog(gui, p, tr("Configure Plugin Sites"), JOptionPane.OK_CANCEL_OPTION); 186 if (answer != JOptionPane.OK_OPTION) 187 return; 188 StringBuilder b = new StringBuilder(); 189 for (int i = 0; i < model.getSize(); ++i) { 190 b.append(model.getElementAt(i)); 191 if (i < model.getSize()-1) 192 b.append(" "); 193 } 194 Main.pref.put("pluginmanager.sites", b.toString()); 195 } 196 197 private void update() { 198 // refresh description 199 int num = PluginDownloader.downloadDescription(); 200 Boolean done = false; 201 refreshPluginPanel(gui); 202 203 Set<PluginDescription> toUpdate = new HashSet<PluginDescription>(); 204 StringBuilder toUpdateStr = new StringBuilder(); 205 for (PluginProxy proxy : Main.plugins) { 206 PluginDescription description = findDescription(proxy.info.name); 207 if (description != null && (description.version == null || description.version.equals("")) 208 ? (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) { 209 toUpdate.add(description); 210 toUpdateStr.append(description.name+"\n"); 211 } 212 } 213 if (toUpdate.isEmpty()) { 214 JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date.")); 215 done = true; 216 } 217 else 218 { 219 int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}", 220 toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION); 221 if (answer == JOptionPane.OK_OPTION) 222 { 223 PluginDownloader.update(toUpdate); 224 done = true; 225 } 226 } 227 if(done && num >= 1) 228 Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis())); 229 } 230 231 private PluginDescription findDescription(String name) { 232 for (PluginDescription d : pluginMap.keySet()) 233 if (d.name.equals(name)) 234 return d; 235 return null; 236 } 237 238 private void refreshPluginPanel(final PreferenceDialog gui) { 239 Collection<PluginDescription> availablePlugins = getAvailablePlugins(); 240 pluginMap = new HashMap<PluginDescription, Boolean>(); 241 pluginPanel.removeAll(); 242 int width = pluginPanel.myGetWidth(); 243 244 Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null); 245 246 for (final PluginDescription plugin : availablePlugins) { 247 boolean enabled = enabledPlugins != null && enabledPlugins.contains(plugin.name); 248 String remoteversion = plugin.version; 249 if(remoteversion == null || remoteversion.equals("")) 250 remoteversion = tr("unknown"); 251 252 String localversion; 253 PluginInformation p = PluginInformation.findPlugin(plugin.name); 254 if(p != null) 255 { 256 if(p.version != null && !p.version.equals("")) 257 localversion = p.version; 258 else 259 localversion = tr("unknown"); 260 localversion = " (" + localversion + ")"; 261 } 262 else 263 localversion = ""; 264 265 final JCheckBox pluginCheck = new JCheckBox(tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion), enabled); 266 pluginPanel.add(pluginCheck); 267 268 pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM")); 269 JLabel label = new JLabel("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>"); 270 label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0)); 271 label.setMaximumSize(new Dimension(width,1000)); 272 pluginPanel.add(label); 273 pluginPanel.add(Box.createVerticalStrut(5)); 274 275 pluginMap.put(plugin, enabled); 276 pluginCheck.addActionListener(new ActionListener(){ 277 public void actionPerformed(ActionEvent e) { 278 278 // if user enabled a plugin, it is not loaded but found somewhere on disk: offer to delete jar 279 279 if (pluginCheck.isSelected()) { … … 281 281 if ((PluginInformation.getLoaded(plugin.name) == null) && (plinfo != null)) { 282 282 try { 283 int answer = JOptionPane.showConfirmDialog(Main.parent, 283 int answer = JOptionPane.showConfirmDialog(Main.parent, 284 284 tr("Plugin archive already available. Do you want to download current version by deleting existing archive?\n\n{0}", 285 285 plinfo.file.getCanonicalPath()), tr("Plugin already exists"), JOptionPane.OK_CANCEL_OPTION); … … 295 295 } 296 296 } 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 proxy.info.name, 346 proxy.info.description, 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 int answer = JOptionPane.showConfirmDialog(Main.parent, 364 tr("Download the following plugins?\n\n{0}", msg), 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 297 pluginMap.put(plugin, pluginCheck.isSelected()); 298 } 299 }); 300 } 301 plugin.updateUI(); 302 } 303 304 private Collection<PluginDescription> getAvailablePlugins() { 305 SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){ 306 public int compare(String o1, String o2) { 307 return o1.compareToIgnoreCase(o2); 308 } 309 }); 310 for (String location : PluginInformation.getPluginLocations()) { 311 File[] pluginFiles = new File(location).listFiles(); 312 if (pluginFiles != null) { 313 Arrays.sort(pluginFiles); 314 for (File f : pluginFiles) { 315 if (!f.isFile()) 316 continue; 317 if (f.getName().endsWith(".jar")) { 318 try { 319 PluginInformation info = new PluginInformation(f); 320 if (!availablePlugins.containsKey(info.name)) 321 availablePlugins.put(info.name, new PluginDescription( 322 info.name, 323 info.description, 324 PluginInformation.fileToURL(f).toString(), 325 info.version)); 326 } catch (PluginException x) { 327 } 328 } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) { 329 try { 330 Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class); 331 for (PluginDescription pd : parser) 332 if (!availablePlugins.containsKey(pd.name)) 333 availablePlugins.put(pd.name, pd); 334 } catch (Exception e) { 335 e.printStackTrace(); 336 JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName())); 337 } 338 } 339 } 340 } 341 } 342 for (PluginProxy proxy : Main.plugins) 343 if (!availablePlugins.containsKey(proxy.info.name)) 344 availablePlugins.put(proxy.info.name, new PluginDescription( 345 proxy.info.name, 346 proxy.info.description, 347 proxy.info.file == null ? null : 348 PluginInformation.fileToURL(proxy.info.file).toString(), 349 proxy.info.version)); 350 return availablePlugins.values(); 351 } 352 353 public void ok() { 354 Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>(); 355 String msg = ""; 356 for (Entry<PluginDescription, Boolean> entry : pluginMap.entrySet()) { 357 if (entry.getValue() && PluginInformation.findPlugin(entry.getKey().name) == null) { 358 toDownload.add(entry.getKey()); 359 msg += entry.getKey().name+"\n"; 360 } 361 } 362 if (!toDownload.isEmpty()) { 363 int answer = JOptionPane.showConfirmDialog(Main.parent, 364 tr("Download the following plugins?\n\n{0}", msg), 365 tr("Download missing plugins"), 366 JOptionPane.YES_NO_OPTION); 367 if (answer != JOptionPane.OK_OPTION) 368 for (PluginDescription pd : toDownload) 369 pluginMap.put(pd, false); 370 else 371 for (PluginDescription pd : toDownload) 372 if (!PluginDownloader.downloadPlugin(pd)) 373 pluginMap.put(pd, false); 374 375 } 376 377 String oldPlugins = Main.pref.get("plugins"); 378 LinkedList<String> plugins = new LinkedList<String>(); 379 Object pd[] = pluginMap.keySet().toArray(); 380 Arrays.sort(pd); 381 for (Object d : pd) { 382 if (pluginMap.get(d)) 383 plugins.add(((PluginDescription)d).name); 384 } 385 386 Main.pref.putCollection("plugins", plugins); 387 String newPlugins = Main.pref.get("plugins"); 388 if(oldPlugins == null && plugins == null) 389 return; 390 if(plugins == null || oldPlugins == null || !plugins.equals(oldPlugins)) 391 gui.requiresRestart = true; 392 } 393 393 } -
trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java
r1165 r1169 34 34 public class PreferenceDialog extends JTabbedPane { 35 35 36 36 public final static Collection<PreferenceSetting> settings = new LinkedList<PreferenceSetting>(); 37 37 38 39 38 public boolean requiresRestart = false; 39 public final RequireRestartAction requireRestartAction = new RequireRestartAction(); 40 40 41 42 43 44 45 41 // some common tabs 42 public final JPanel display = createPreferenceTab("display", tr("Display Settings"), tr("Various settings that influence the visual representation of the whole program.")); 43 public final JPanel connection = createPreferenceTab("connection", I18n.tr("Connection Settings"), I18n.tr("Connection Settings for the OSM server.")); 44 public final JPanel map = createPreferenceTab("map", I18n.tr("Map Settings"), I18n.tr("Settings for the map projection and data interpretation.")); 45 public final JPanel audio = createPreferenceTab("audio", I18n.tr("Audio Settings"), I18n.tr("Settings for the audio player and audio markers.")); 46 46 47 47 public final javax.swing.JTabbedPane displaycontent = new javax.swing.JTabbedPane(); 48 48 49 50 51 * and a centered title label and the description are added. The panel 52 * will be shown inside a {@link ScrollPane} 53 54 55 56 *italic under the title.57 58 59 60 61 49 /** 50 * Construct a JPanel for the preference settings. Layout is GridBagLayout 51 * and a centered title label and the description are added. The panel 52 * will be shown inside a {@link ScrollPane} 53 * @param icon The name of the icon. 54 * @param title The title of this preference tab. 55 * @param desc A description in one sentence for this tab. Will be displayed 56 * italic under the title. 57 * @return The created panel ready to add other controls. 58 */ 59 public JPanel createPreferenceTab(String icon, String title, String desc) { 60 return createPreferenceTab(icon, title, desc, true); 61 } 62 62 63 63 /** … … 68 68 * @param desc A description in one sentence for this tab. Will be displayed 69 69 * italic under the title. 70 * @param inScrollPane if <code>true</code> the added tab will show scroll bars 70 * @param inScrollPane if <code>true</code> the added tab will show scroll bars 71 71 * if the panel content is larger than the available space 72 72 * @return The created panel ready to add other controls. … … 93 93 94 94 95 96 97 98 99 95 private final class RequireRestartAction implements ActionListener { 96 public void actionPerformed(ActionEvent e) { 97 requiresRestart = true; 98 } 99 } 100 100 101 102 103 104 105 106 107 101 public void ok() { 102 for (PreferenceSetting setting : settings) 103 setting.ok(); 104 if (requiresRestart) 105 JOptionPane.showMessageDialog(Main.parent,tr("You have to restart JOSM for some settings to take effect.")); 106 Main.parent.repaint(); 107 } 108 108 109 110 111 112 113 114 115 116 117 118 109 /** 110 * If the dialog is closed with Ok, the preferences will be stored to the preferences- 111 * file, otherwise no change of the file happens. 112 */ 113 public PreferenceDialog() { 114 super(JTabbedPane.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT); 115 display.add(displaycontent, GBC.eol().fill(GBC.BOTH)); 116 for (Iterator<PreferenceSetting> it = settings.iterator(); it.hasNext();) { 117 try { 118 it.next().addGui(this); 119 119 } catch (SecurityException e) { 120 120 it.remove(); 121 121 } 122 123 122 } 123 } 124 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 125 static { 126 // order is important! 127 settings.add(new DrawingPreference()); 128 settings.add(new ColorPreference()); 129 settings.add(new LafPreference()); 130 settings.add(new LanguagePreference()); 131 settings.add(new MapPaintPreference()); 132 settings.add(new ServerAccessPreference()); 133 settings.add(new FilePreferences()); 134 settings.add(new ProxyPreferences()); 135 settings.add(new ProjectionPreference()); 136 settings.add(new TaggingPresetPreference()); 137 settings.add(new PluginPreference()); 138 settings.add(Main.toolbar); 139 settings.add(new AudioPreference()); 140 settings.add(new ShortcutPreference()); 141 141 142 143 144 145 146 142 for (PluginProxy plugin : Main.plugins) { 143 PreferenceSetting p = plugin.getPreferenceSetting(); 144 if (p != null) 145 settings.add(p); 146 } 147 147 148 149 150 148 // always the last: advanced tab 149 settings.add(new AdvancedPreference()); 150 } 151 151 } -
trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceSetting.java
r999 r1169 4 4 5 5 public interface PreferenceSetting { 6 7 8 9 10 6 /** 7 * Add the GUI elements to the dialog. The elements should be initialized after 8 * the current preferences. 9 */ 10 void addGui(PreferenceDialog gui); 11 11 12 13 14 15 12 /** 13 * Called when OK is pressed to save the setting in the preferences file. 14 */ 15 void ok(); 16 16 } -
trunk/src/org/openstreetmap/josm/gui/preferences/ProjectionPreference.java
r1108 r1169 19 19 public class ProjectionPreference implements PreferenceSetting { 20 20 21 22 23 24 25 21 /** 22 * Combobox with all projections available 23 */ 24 private JComboBox projectionCombo = new JComboBox(Projection.allProjections); 25 private JComboBox coordinatesCombo = new JComboBox(CoordinateFormat.values()); 26 26 27 public void addGui(PreferenceDialog gui) { 28 29 for (int i = 0; i < projectionCombo.getItemCount(); ++i) { 30 if (projectionCombo.getItemAt(i).getClass().getName().equals(Main.pref.get("projection"))) { 31 projectionCombo.setSelectedIndex(i); 32 break; 33 } 34 } 27 public void addGui(PreferenceDialog gui) { 35 28 36 for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {37 if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(Main.pref.get("coordinates"))) {38 coordinatesCombo.setSelectedIndex(i);39 40 41 29 for (int i = 0; i < projectionCombo.getItemCount(); ++i) { 30 if (projectionCombo.getItemAt(i).getClass().getName().equals(Main.pref.get("projection"))) { 31 projectionCombo.setSelectedIndex(i); 32 break; 33 } 34 } 42 35 43 projectionCombo.addActionListener(gui.requireRestartAction); 36 for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) { 37 if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(Main.pref.get("coordinates"))) { 38 coordinatesCombo.setSelectedIndex(i); 39 break; 40 } 41 } 42 43 projectionCombo.addActionListener(gui.requireRestartAction); 44 44 coordinatesCombo.addActionListener(gui.requireRestartAction); 45 46 47 48 49 50 51 52 53 54 55 45 46 JPanel projPanel = new JPanel(); 47 projPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Map Projection"))); 48 projPanel.setLayout(new GridBagLayout()); 49 projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5)); 50 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 51 projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 52 projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5)); 53 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 54 projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 55 gui.map.add(projPanel, GBC.eol().insets(0,0,0,10).fill(GBC.HORIZONTAL)); 56 56 } 57 57 58 59 60 58 public void ok() { 59 Main.pref.put("projection", projectionCombo.getSelectedItem().getClass().getName()); 60 Main.pref.put("coordinates", ((CoordinateFormat)coordinatesCombo.getSelectedItem()).name()); 61 61 } 62 62 } -
trunk/src/org/openstreetmap/josm/gui/preferences/ProxyPreferences.java
r1053 r1169 20 20 public class ProxyPreferences implements PreferenceSetting { 21 21 22 23 24 25 26 27 22 public static final String PROXY_ENABLE = "proxy.enable"; 23 public static final String PROXY_HOST = "proxy.host"; 24 public static final String PROXY_PORT = "proxy.port"; 25 public static final String PROXY_ANONYMOUS = "proxy.anonymous"; 26 public static final String PROXY_USER = "proxy.user"; 27 public static final String PROXY_PASS = "proxy.pass"; 28 28 29 30 31 32 33 34 29 private JCheckBox proxyEnable = new JCheckBox(tr("Enable proxy server")); 30 private JTextField proxyHost = new JTextField(20); 31 private JTextField proxyPort = new JTextField(5); 32 private JCheckBox proxyAnonymous = new JCheckBox(tr("Anonymous")); 33 private JTextField proxyUser = new JTextField(20); 34 private JPasswordField proxyPass = new JPasswordField(20); 35 35 36 public void addGui(PreferenceDialog gui) { 37 proxyEnable.setSelected(Main.pref.getBoolean(PROXY_ENABLE)); 38 proxyEnable.addActionListener(new ActionListener(){ 39 public void actionPerformed(ActionEvent e) { 40 proxyHost.setEnabled(proxyEnable.isSelected()); 41 proxyPort.setEnabled(proxyEnable.isSelected()); 42 proxyAnonymous.setEnabled(proxyEnable.isSelected()); 43 proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 44 proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 45 } 46 }); 47 proxyHost.setEnabled(Main.pref.getBoolean(PROXY_ENABLE)); 48 proxyHost.setText(Main.pref.get(PROXY_HOST)); 49 proxyPort.setEnabled(Main.pref.getBoolean(PROXY_ENABLE)); 50 proxyPort.setText(Main.pref.get(PROXY_PORT)); 51 proxyAnonymous.setEnabled(Main.pref.getBoolean(PROXY_ENABLE)); 52 proxyAnonymous.setSelected(Main.pref.getBoolean(PROXY_ANONYMOUS)); 53 proxyAnonymous.addActionListener(new ActionListener(){ 54 public void actionPerformed(ActionEvent e) { 55 proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 56 proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 57 } 58 }); 59 proxyUser.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS))); 60 proxyUser.setText(Main.pref.get(PROXY_USER)); 61 proxyPass.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS))); 62 proxyPass.setText(Main.pref.get(PROXY_USER)); 63 64 gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL)); 65 gui.connection.add(new JLabel(tr("Proxy Settings")), GBC.eol()); 66 gui.connection.add(proxyEnable, GBC.eol().insets(20, 0, 0, 0)); 67 gui.connection.add(new JLabel(tr("Proxy server host")), GBC.std()); 68 gui.connection.add(proxyHost, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 69 gui.connection.add(new JLabel(tr("Proxy server port")), GBC.std()); 70 gui.connection.add(proxyPort, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 71 gui.connection.add(proxyAnonymous, GBC.eop().insets(20, 0, 0, 0)); 72 gui.connection.add(new JLabel(tr("Proxy server username")), GBC.std()); 73 gui.connection.add(proxyUser, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 74 gui.connection.add(new JLabel(tr("Proxy server password")), GBC.std()); 75 gui.connection.add(proxyPass, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 36 public void addGui(PreferenceDialog gui) { 37 proxyEnable.setSelected(Main.pref.getBoolean(PROXY_ENABLE)); 38 proxyEnable.addActionListener(new ActionListener(){ 39 public void actionPerformed(ActionEvent e) { 40 proxyHost.setEnabled(proxyEnable.isSelected()); 41 proxyPort.setEnabled(proxyEnable.isSelected()); 42 proxyAnonymous.setEnabled(proxyEnable.isSelected()); 43 proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 44 proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 45 } 46 }); 47 proxyHost.setEnabled(Main.pref.getBoolean(PROXY_ENABLE)); 48 proxyHost.setText(Main.pref.get(PROXY_HOST)); 49 proxyPort.setEnabled(Main.pref.getBoolean(PROXY_ENABLE)); 50 proxyPort.setText(Main.pref.get(PROXY_PORT)); 51 proxyAnonymous.setEnabled(Main.pref.getBoolean(PROXY_ENABLE)); 52 proxyAnonymous.setSelected(Main.pref.getBoolean(PROXY_ANONYMOUS)); 53 proxyAnonymous.addActionListener(new ActionListener(){ 54 public void actionPerformed(ActionEvent e) { 55 proxyUser.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 56 proxyPass.setEnabled(proxyEnable.isSelected() && !proxyAnonymous.isSelected()); 57 } 58 }); 59 proxyUser.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS))); 60 proxyUser.setText(Main.pref.get(PROXY_USER)); 61 proxyPass.setEnabled(Main.pref.getBoolean(PROXY_ENABLE) && (Main.pref.getBoolean(PROXY_ANONYMOUS))); 62 proxyPass.setText(Main.pref.get(PROXY_USER)); 76 63 77 gui.connection.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL)); 78 } 64 gui.connection.add(new JSeparator(SwingConstants.HORIZONTAL), GBC.eol().fill(GBC.HORIZONTAL)); 65 gui.connection.add(new JLabel(tr("Proxy Settings")), GBC.eol()); 66 gui.connection.add(proxyEnable, GBC.eol().insets(20, 0, 0, 0)); 67 gui.connection.add(new JLabel(tr("Proxy server host")), GBC.std()); 68 gui.connection.add(proxyHost, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 69 gui.connection.add(new JLabel(tr("Proxy server port")), GBC.std()); 70 gui.connection.add(proxyPort, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 71 gui.connection.add(proxyAnonymous, GBC.eop().insets(20, 0, 0, 0)); 72 gui.connection.add(new JLabel(tr("Proxy server username")), GBC.std()); 73 gui.connection.add(proxyUser, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 74 gui.connection.add(new JLabel(tr("Proxy server password")), GBC.std()); 75 gui.connection.add(proxyPass, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 79 76 80 public void ok() { 81 Main.pref.put(PROXY_ENABLE, proxyEnable.isSelected()); 82 Main.pref.put(PROXY_HOST, proxyHost.getText()); 83 Main.pref.put(PROXY_PORT, proxyPort.getText()); 84 Main.pref.put(PROXY_ANONYMOUS, proxyAnonymous.isSelected()); 85 Main.pref.put(PROXY_USER, proxyUser.getText()); 86 Main.pref.put(PROXY_PASS, new String(proxyPass.getPassword())); 87 } 77 gui.connection.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.VERTICAL)); 78 } 79 80 public void ok() { 81 Main.pref.put(PROXY_ENABLE, proxyEnable.isSelected()); 82 Main.pref.put(PROXY_HOST, proxyHost.getText()); 83 Main.pref.put(PROXY_PORT, proxyPort.getText()); 84 Main.pref.put(PROXY_ANONYMOUS, proxyAnonymous.isSelected()); 85 Main.pref.put(PROXY_USER, proxyUser.getText()); 86 Main.pref.put(PROXY_PASS, new String(proxyPass.getPassword())); 87 } 88 88 89 89 } -
trunk/src/org/openstreetmap/josm/gui/preferences/ServerAccessPreference.java
r627 r1169 15 15 public class ServerAccessPreference implements PreferenceSetting { 16 16 17 18 19 20 21 22 23 24 25 26 27 28 17 /** 18 * Editfield for the Base url to the REST API from OSM. 19 */ 20 private JTextField osmDataServer = new JTextField(20); 21 /** 22 * Editfield for the username to the OSM account. 23 */ 24 private JTextField osmDataUsername = new JTextField(20); 25 /** 26 * Passwordfield for the userpassword of the REST API. 27 */ 28 private JPasswordField osmDataPassword = new JPasswordField(20); 29 29 30 31 32 33 30 public void addGui(PreferenceDialog gui) { 31 osmDataServer.setText(Main.pref.get("osm-server.url")); 32 osmDataUsername.setText(Main.pref.get("osm-server.username")); 33 osmDataPassword.setText(Main.pref.get("osm-server.password")); 34 34 35 36 37 35 osmDataServer.setToolTipText(tr("The base URL for the OSM server (REST API)")); 36 osmDataUsername.setToolTipText(tr("Login name (email) to the OSM account.")); 37 osmDataPassword.setToolTipText(tr("Login password to the OSM account. Leave blank to not store any password.")); 38 38 39 40 41 42 43 44 45 46 47 48 49 50 51 39 gui.connection.add(new JLabel(tr("Base Server URL")), GBC.std()); 40 gui.connection.add(osmDataServer, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 41 gui.connection.add(new JLabel(tr("OSM username (email)")), GBC.std()); 42 gui.connection.add(osmDataUsername, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5)); 43 gui.connection.add(new JLabel(tr("OSM password")), GBC.std()); 44 gui.connection.add(osmDataPassword, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,0)); 45 JLabel warning = new JLabel(tr("<html>" + 46 "WARNING: The password is stored in plain text in the preferences file.<br>" + 47 "The password is transfered in plain text to the server, encoded in the url.<br>" + 48 "<b>Do not use a valuable Password.</b></html>")); 49 warning.setFont(warning.getFont().deriveFont(Font.ITALIC)); 50 gui.connection.add(warning, GBC.eop().fill(GBC.HORIZONTAL)); 51 } 52 52 53 54 55 56 57 58 59 53 public void ok() { 54 Main.pref.put("osm-server.url", osmDataServer.getText()); 55 Main.pref.put("osm-server.username", osmDataUsername.getText()); 56 String pwd = String.valueOf(osmDataPassword.getPassword()); 57 if (pwd.equals("")) 58 pwd = null; 59 Main.pref.put("osm-server.password", pwd); 60 60 } 61 61 } -
trunk/src/org/openstreetmap/josm/gui/preferences/ShortcutPreference.java
r1084 r1169 15 15 public class ShortcutPreference implements PreferenceSetting { 16 16 17 18 19 20 21 22 23 24 JPanel p = gui.createPreferenceTab("shortcuts", tr("Shortcut Preferences"), 25 17 public void addGui(PreferenceDialog gui) { 18 // icon source: http://www.iconfinder.net/index.php?q=key&page=icondetails&iconid=8553&size=128&q=key&s12=on&s16=on&s22=on&s32=on&s48=on&s64=on&s128=on 19 // icon licence: GPL 20 // icon designer: Paolino, http://www.paolinoland.it/ 21 // icon original filename: keyboard.png 22 // icon original size: 128x128 23 // modifications: icon was cropped, then resized 24 JPanel p = gui.createPreferenceTab("shortcuts", tr("Shortcut Preferences"), 25 tr("Changing keyboard shortcuts manually."), false); 26 26 27 28 27 prefJPanel prefpanel = new prefJPanel(new scListModel()); 28 p.add(prefpanel, GBC.eol().fill(GBC.BOTH)); 29 29 30 30 } 31 31 32 32 public void ok() { 33 33 } 34 34 35 36 37 // 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 35 // Maybe move this to prefPanel? There's no need for it to be here. 36 private class scListModel extends AbstractTableModel { 37 // private String[] columnNames = new String[]{tr("Action"), tr("Shortcut"), tr("Group"), tr("ID")}; 38 private String[] columnNames = new String[]{tr("Action"), tr("Shortcut")}; 39 private Collection<Shortcut> data; 40 public scListModel() { 41 data = Shortcut.listAll(); 42 } 43 public int getColumnCount() { 44 return columnNames.length; 45 } 46 public int getRowCount() { 47 return data.size(); 48 } 49 public String getColumnName(int col) { 50 return columnNames[col]; 51 } 52 public Object getValueAt(int row, int col) { 53 Shortcut sc = (Shortcut)data.toArray()[row]; 54 if (col == 0) { 55 return sc.getLongText(); 56 } else if (col == 1) { 57 return sc.getKeyText(); 58 } /*else if (col == 2) { 59 if (sc.getRequestedGroup() == Shortcut.GROUP_NONE) { 60 return tr("None"); 61 } else if (sc.getRequestedGroup() == Shortcut.GROUP_HOTKEY) { 62 return tr("Hotkey"); 63 } else if (sc.getRequestedGroup() == Shortcut.GROUP_MENU) { 64 return tr("Menu"); 65 } else if (sc.getRequestedGroup() == Shortcut.GROUP_EDIT) { 66 return tr("Edit"); 67 } else if (sc.getRequestedGroup() == Shortcut.GROUP_LAYER) { 68 return tr("Subwindow"); 69 } else if (sc.getRequestedGroup() == Shortcut.GROUP_DIRECT) { 70 return tr("Direct"); 71 } else if (sc.getRequestedGroup() == Shortcut.GROUP_MNEMONIC) { 72 return tr("Mnemonic"); 73 } else if (sc.getRequestedGroup() == Shortcut.GROUP_RESERVED) { 74 return tr("System"); 75 } else { 76 return tr("Unknown"); 77 } 78 } else if (col == 3) { 79 return sc.getShortText(); 80 } */else { 81 // This is a kind of hack that allows the actions on the editing controls 82 // to access the underlying shortcut object without introducing another 83 // method. I opted to stay within the interface. 84 return sc; 85 } 86 } 87 public boolean isCellEditable(int row, int col) { 88 return false; 89 } 90 } 91 91 92 92 } -
trunk/src/org/openstreetmap/josm/gui/preferences/TaggingPresetPreference.java
r999 r1169 34 34 public class TaggingPresetPreference implements PreferenceSetting { 35 35 36 public static Collection<TaggingPreset> taggingPresets; 37 private JList taggingPresetSources; 38 private JCheckBox enableDefault; 39 40 public void addGui(final PreferenceDialog gui) { 41 42 taggingPresetSources = new JList(new DefaultListModel()); 43 enableDefault = new JCheckBox(tr("Enable built-in defaults"), 44 Main.pref.getBoolean("taggingpreset.enable-defaults", true)); 36 public static Collection<TaggingPreset> taggingPresets; 37 private JList taggingPresetSources; 38 private JCheckBox enableDefault; 45 39 46 String annos = Main.pref.get("taggingpreset.sources"); 47 StringTokenizer st = new StringTokenizer(annos, ";"); 48 while (st.hasMoreTokens()) 49 ((DefaultListModel)taggingPresetSources.getModel()).addElement(st.nextToken()); 40 public void addGui(final PreferenceDialog gui) { 50 41 51 JButton addAnno = new JButton(tr("Add")); 52 addAnno.addActionListener(new ActionListener(){ 53 public void actionPerformed(ActionEvent e) { 54 String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source")); 55 if (source == null) 56 return; 57 ((DefaultListModel)taggingPresetSources.getModel()).addElement(source); 58 gui.requiresRestart = true; 59 } 60 }); 42 taggingPresetSources = new JList(new DefaultListModel()); 43 enableDefault = new JCheckBox(tr("Enable built-in defaults"), 44 Main.pref.getBoolean("taggingpreset.enable-defaults", true)); 61 45 62 JButton editAnno = new JButton(tr("Edit")); 63 editAnno.addActionListener(new ActionListener(){ 64 public void actionPerformed(ActionEvent e) { 65 if (taggingPresetSources.getSelectedIndex() == -1) 66 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit.")); 67 else { 68 String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source"), taggingPresetSources.getSelectedValue()); 69 if (source == null) 70 return; 71 ((DefaultListModel)taggingPresetSources.getModel()).setElementAt(source, taggingPresetSources.getSelectedIndex()); 72 gui.requiresRestart = true; 73 } 74 } 75 }); 46 String annos = Main.pref.get("taggingpreset.sources"); 47 StringTokenizer st = new StringTokenizer(annos, ";"); 48 while (st.hasMoreTokens()) 49 ((DefaultListModel)taggingPresetSources.getModel()).addElement(st.nextToken()); 76 50 77 JButton deleteAnno = new JButton(tr("Delete")); 78 deleteAnno.addActionListener(new ActionListener(){ 79 public void actionPerformed(ActionEvent e) { 80 if (taggingPresetSources.getSelectedIndex() == -1) 81 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete.")); 82 else { 83 ((DefaultListModel)taggingPresetSources.getModel()).remove(taggingPresetSources.getSelectedIndex()); 84 gui.requiresRestart = true; 85 } 86 } 87 }); 88 taggingPresetSources.setVisibleRowCount(3); 51 JButton addAnno = new JButton(tr("Add")); 52 addAnno.addActionListener(new ActionListener(){ 53 public void actionPerformed(ActionEvent e) { 54 String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source")); 55 if (source == null) 56 return; 57 ((DefaultListModel)taggingPresetSources.getModel()).addElement(source); 58 gui.requiresRestart = true; 59 } 60 }); 89 61 90 taggingPresetSources.setToolTipText(tr("The sources (url or filename) of tagging preset definition files. See http://josm.openstreetmap.de/wiki/TaggingPresets for help.")); 91 addAnno.setToolTipText(tr("Add a new tagging preset source to the list.")); 92 deleteAnno.setToolTipText(tr("Delete the selected source from the list.")); 62 JButton editAnno = new JButton(tr("Edit")); 63 editAnno.addActionListener(new ActionListener(){ 64 public void actionPerformed(ActionEvent e) { 65 if (taggingPresetSources.getSelectedIndex() == -1) 66 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit.")); 67 else { 68 String source = JOptionPane.showInputDialog(Main.parent, tr("Tagging preset source"), taggingPresetSources.getSelectedValue()); 69 if (source == null) 70 return; 71 ((DefaultListModel)taggingPresetSources.getModel()).setElementAt(source, taggingPresetSources.getSelectedIndex()); 72 gui.requiresRestart = true; 73 } 74 } 75 }); 93 76 94 JPanel tpPanel = new JPanel(); 95 tpPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Tagging Presets"))); 96 tpPanel.setLayout(new GridBagLayout()); 97 tpPanel.add(enableDefault, GBC.eol().insets(5,5,5,0)); 98 tpPanel.add(new JLabel(tr("Tagging preset sources")), GBC.eol().insets(5,5,5,0)); 99 tpPanel.add(new JScrollPane(taggingPresetSources), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH)); 100 JPanel buttonPanel = new JPanel(new GridBagLayout()); 101 tpPanel.add(buttonPanel, GBC.eol().insets(5,0,5,5).fill(GBC.HORIZONTAL)); 102 buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); 103 buttonPanel.add(addAnno, GBC.std().insets(0,5,0,0)); 104 buttonPanel.add(editAnno, GBC.std().insets(5,5,5,0)); 105 buttonPanel.add(deleteAnno, GBC.std().insets(0,5,0,0)); 106 gui.map.add(tpPanel, GBC.eol().fill(GBC.BOTH)); 107 } 77 JButton deleteAnno = new JButton(tr("Delete")); 78 deleteAnno.addActionListener(new ActionListener(){ 79 public void actionPerformed(ActionEvent e) { 80 if (taggingPresetSources.getSelectedIndex() == -1) 81 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete.")); 82 else { 83 ((DefaultListModel)taggingPresetSources.getModel()).remove(taggingPresetSources.getSelectedIndex()); 84 gui.requiresRestart = true; 85 } 86 } 87 }); 88 taggingPresetSources.setVisibleRowCount(3); 108 89 109 public void ok() { 110 Main.pref.put("taggingpreset.enable-defaults", enableDefault.getSelectedObjects() != null); 111 if (taggingPresetSources.getModel().getSize() > 0) { 112 StringBuilder sb = new StringBuilder(); 113 for (int i = 0; i < taggingPresetSources.getModel().getSize(); ++i) 114 sb.append(";"+taggingPresetSources.getModel().getElementAt(i)); 115 Main.pref.put("taggingpreset.sources", sb.toString().substring(1)); 116 } else 117 Main.pref.put("taggingpreset.sources", null); 118 } 90 taggingPresetSources.setToolTipText(tr("The sources (url or filename) of tagging preset definition files. See http://josm.openstreetmap.de/wiki/TaggingPresets for help.")); 91 addAnno.setToolTipText(tr("Add a new tagging preset source to the list.")); 92 deleteAnno.setToolTipText(tr("Delete the selected source from the list.")); 119 93 120 /** 121 * Initialize the tagging presets (load and may display error) 122 */ 123 public static void initialize() { 124 taggingPresets = TaggingPreset.readFromPreferences(); 125 if (taggingPresets.isEmpty()) { 126 Main.main.menu.presetsMenu.setVisible(false); 127 } 128 else 129 { 130 HashMap<TaggingPresetMenu,JMenu> submenus = new HashMap<TaggingPresetMenu,JMenu>(); 131 for (final TaggingPreset p : taggingPresets) 132 { 133 JMenu m = p.group != null ? submenus.get(p.group) : Main.main.menu.presetsMenu; 134 if (p instanceof TaggingPresetSeparator) 135 m.add(new JSeparator()); 136 else if (p instanceof TaggingPresetMenu) 137 { 138 JMenu submenu = new JMenu(p); 139 ((TaggingPresetMenu)p).menu = submenu; 140 submenus.put((TaggingPresetMenu)p, submenu); 141 m.add(submenu); 142 } 143 else 144 { 145 JMenuItem mi = new JMenuItem(p); 146 mi.setText(tr(p.name)); 147 m.add(mi); 148 } 149 } 150 } 151 } 94 JPanel tpPanel = new JPanel(); 95 tpPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray), tr("Tagging Presets"))); 96 tpPanel.setLayout(new GridBagLayout()); 97 tpPanel.add(enableDefault, GBC.eol().insets(5,5,5,0)); 98 tpPanel.add(new JLabel(tr("Tagging preset sources")), GBC.eol().insets(5,5,5,0)); 99 tpPanel.add(new JScrollPane(taggingPresetSources), GBC.eol().insets(5,0,5,0).fill(GBC.BOTH)); 100 JPanel buttonPanel = new JPanel(new GridBagLayout()); 101 tpPanel.add(buttonPanel, GBC.eol().insets(5,0,5,5).fill(GBC.HORIZONTAL)); 102 buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); 103 buttonPanel.add(addAnno, GBC.std().insets(0,5,0,0)); 104 buttonPanel.add(editAnno, GBC.std().insets(5,5,5,0)); 105 buttonPanel.add(deleteAnno, GBC.std().insets(0,5,0,0)); 106 gui.map.add(tpPanel, GBC.eol().fill(GBC.BOTH)); 107 } 108 109 public void ok() { 110 Main.pref.put("taggingpreset.enable-defaults", enableDefault.getSelectedObjects() != null); 111 if (taggingPresetSources.getModel().getSize() > 0) { 112 StringBuilder sb = new StringBuilder(); 113 for (int i = 0; i < taggingPresetSources.getModel().getSize(); ++i) 114 sb.append(";"+taggingPresetSources.getModel().getElementAt(i)); 115 Main.pref.put("taggingpreset.sources", sb.toString().substring(1)); 116 } else 117 Main.pref.put("taggingpreset.sources", null); 118 } 119 120 /** 121 * Initialize the tagging presets (load and may display error) 122 */ 123 public static void initialize() { 124 taggingPresets = TaggingPreset.readFromPreferences(); 125 if (taggingPresets.isEmpty()) { 126 Main.main.menu.presetsMenu.setVisible(false); 127 } 128 else 129 { 130 HashMap<TaggingPresetMenu,JMenu> submenus = new HashMap<TaggingPresetMenu,JMenu>(); 131 for (final TaggingPreset p : taggingPresets) 132 { 133 JMenu m = p.group != null ? submenus.get(p.group) : Main.main.menu.presetsMenu; 134 if (p instanceof TaggingPresetSeparator) 135 m.add(new JSeparator()); 136 else if (p instanceof TaggingPresetMenu) 137 { 138 JMenu submenu = new JMenu(p); 139 ((TaggingPresetMenu)p).menu = submenu; 140 submenus.put((TaggingPresetMenu)p, submenu); 141 m.add(submenu); 142 } 143 else 144 { 145 JMenuItem mi = new JMenuItem(p); 146 mi.setText(tr(p.name)); 147 m.add(mi); 148 } 149 } 150 } 151 } 152 152 } -
trunk/src/org/openstreetmap/josm/gui/preferences/ToolbarPreferences.java
r1053 r1169 37 37 public class ToolbarPreferences implements PreferenceSetting { 38 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 JPanel panel = gui.createPreferenceTab("toolbar", tr("Toolbar customization"), 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 * 265 * Call this, if anything has changed in the toolbar settings and you want to refresh 266 267 268 269 270 271 272 273 274 275 276 277 39 private final class Move implements ActionListener { 40 public void actionPerformed(ActionEvent e) { 41 if (e.getActionCommand().equals("<<")) { 42 while (unselected.size() > 1) { 43 selected.addElement(unselected.get(0)); 44 unselected.remove(0); 45 } 46 } else if (e.getActionCommand().equals("<") && unselectedList.getSelectedIndex() != -1) { 47 while (unselectedList.getSelectedIndex() != -1 && unselectedList.getSelectedIndex() != unselected.size()-1) { 48 selected.addElement(unselectedList.getSelectedValue()); 49 unselected.remove(unselectedList.getSelectedIndex()); 50 } 51 if (unselectedList.getSelectedIndex() == unselected.size()-1) 52 selected.addElement(null); 53 } else if (e.getActionCommand().equals(">") && selectedList.getSelectedIndex() != -1) { 54 while (selectedList.getSelectedIndex() != -1) { 55 if (selectedList.getSelectedValue() != null) 56 unselected.add(unselected.size()-1, selectedList.getSelectedValue()); 57 selected.remove(selectedList.getSelectedIndex()); 58 } 59 } else if (e.getActionCommand().equals(">>")) { 60 while (selected.size() > 0) { 61 if (selected.get(0) != null) 62 unselected.add(unselected.size()-1, selected.get(0)); 63 selected.remove(0); 64 } 65 } else if (e.getActionCommand().equals("up")) { 66 int i = selectedList.getSelectedIndex(); 67 Object o = selected.get(i); 68 if (i != 0) { 69 selected.remove(i); 70 selected.add(i-1, o); 71 selectedList.setSelectedIndex(i-1); 72 } 73 } else if (e.getActionCommand().equals("down")) { 74 int i = selectedList.getSelectedIndex(); 75 Object o = selected.get(i); 76 if (i != selected.size()-1) { 77 selected.remove(i); 78 selected.add(i+1, o); 79 selectedList.setSelectedIndex(i+1); 80 } 81 } 82 } 83 } 84 private Move moveAction = new Move(); 85 86 /** 87 * Key: Registered name (property "toolbar" of action). 88 * Value: The action to execute. 89 */ 90 private Map<String, Action> actions = new HashMap<String, Action>(); 91 92 private DefaultListModel selected = new DefaultListModel(); 93 private DefaultListModel unselected = new DefaultListModel(); 94 private JList selectedList = new JList(selected); 95 private JList unselectedList = new JList(unselected); 96 97 public JToolBar control = new JToolBar(); 98 99 private JButton upButton; 100 private JButton downButton; 101 102 public ToolbarPreferences() { 103 control.setFloatable(false); 104 105 final ListCellRenderer oldRenderer = selectedList.getCellRenderer(); 106 ListCellRenderer renderer = new DefaultListCellRenderer(){ 107 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 108 String s = tr("Separator"); 109 Icon i = ImageProvider.get("preferences/separator"); 110 if (value != null) { 111 s = (String)((Action)value).getValue(Action.NAME); 112 i = (Icon)((Action)value).getValue(Action.SMALL_ICON); 113 } 114 JLabel l = (JLabel)oldRenderer.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus); 115 l.setIcon(i); 116 return l; 117 } 118 }; 119 selectedList.setCellRenderer(renderer); 120 unselectedList.setCellRenderer(renderer); 121 122 unselectedList.addListSelectionListener(new ListSelectionListener(){ 123 public void valueChanged(ListSelectionEvent e) { 124 if ((unselectedList.getSelectedIndex() != -1)) 125 selectedList.clearSelection(); 126 upButton.setEnabled(selectedList.getSelectedIndex() != -1); 127 downButton.setEnabled(selectedList.getSelectedIndex() != -1); 128 } 129 }); 130 selectedList.addListSelectionListener(new ListSelectionListener(){ 131 public void valueChanged(ListSelectionEvent e) { 132 boolean sel = selectedList.getSelectedIndex() != -1; 133 if (sel) 134 unselectedList.clearSelection(); 135 upButton.setEnabled(sel); 136 downButton.setEnabled(sel); 137 } 138 }); 139 } 140 141 public void addGui(PreferenceDialog gui) { 142 selected.removeAllElements(); 143 unselected.removeAllElements(); 144 Map<String, Action> us = new TreeMap<String, Action>(); 145 for (Action a : actions.values()) 146 { 147 us.put(a.getValue(Action.NAME).toString()+a.toString(), a); 148 } 149 for (String a : us.keySet()) 150 unselected.addElement(us.get(a)); 151 unselected.addElement(null); 152 153 final JPanel left = new JPanel(new GridBagLayout()); 154 left.add(new JLabel(tr("Toolbar")), GBC.eol()); 155 left.add(new JScrollPane(selectedList), GBC.std().fill(GBC.BOTH)); 156 157 final JPanel right = new JPanel(new GridBagLayout()); 158 right.add(new JLabel(tr("Available")), GBC.eol()); 159 right.add(new JScrollPane(unselectedList), GBC.eol().fill(GBC.BOTH)); 160 161 final JPanel buttons = new JPanel(new GridLayout(6,1)); 162 buttons.add(upButton = createButton("up")); 163 buttons.add(createButton("<<")); 164 buttons.add(createButton("<")); 165 buttons.add(createButton(">")); 166 buttons.add(createButton(">>")); 167 buttons.add(downButton = createButton("down")); 168 upButton.setEnabled(false); 169 downButton.setEnabled(false); 170 171 final JPanel p = new JPanel(); 172 p.setLayout(new LayoutManager(){ 173 public void addLayoutComponent(String name, Component comp) {} 174 public void removeLayoutComponent(Component comp) {} 175 public Dimension minimumLayoutSize(Container parent) { 176 Dimension l = left.getMinimumSize(); 177 Dimension r = right.getMinimumSize(); 178 Dimension b = buttons.getMinimumSize(); 179 return new Dimension(l.width+b.width+10+r.width,l.height+b.height+10+r.height); 180 } 181 public Dimension preferredLayoutSize(Container parent) { 182 Dimension l = left.getPreferredSize(); 183 Dimension r = right.getPreferredSize(); 184 return new Dimension(l.width+r.width+10+buttons.getPreferredSize().width,Math.max(l.height, r.height)); 185 } 186 public void layoutContainer(Container parent) { 187 Dimension d = p.getSize(); 188 Dimension b = buttons.getPreferredSize(); 189 int width = (d.width-10-b.width)/2; 190 left.setBounds(new Rectangle(0,0,width,d.height)); 191 right.setBounds(new Rectangle(width+10+b.width,0,width,d.height)); 192 buttons.setBounds(new Rectangle(width+5, d.height/2-b.height/2, b.width, b.height)); 193 } 194 }); 195 p.add(left); 196 p.add(buttons); 197 p.add(right); 198 199 JPanel panel = gui.createPreferenceTab("toolbar", tr("Toolbar customization"), 200 tr("Customize the elements on the toolbar."), false); 201 panel.add(p, GBC.eol().fill(GBC.BOTH)); 202 203 for (String s : getToolString()) { 204 if (s.equals("|")) 205 selected.addElement(null); 206 else { 207 Action a = actions.get(s); 208 if (a != null) { 209 selected.addElement(a); 210 unselected.removeElement(a); 211 } 212 } 213 } 214 } 215 216 private String[] getToolString() { 217 String s = Main.pref.get("toolbar", "open;save;exportgpx;|;download;upload;|;undo;redo;|;preference"); 218 if (s == null || s.equals("null") || s.equals("")) 219 return new String[0]; 220 return s.split(";"); 221 } 222 223 private JButton createButton(String name) { 224 JButton b = new JButton(); 225 if (name.equals("up")) 226 b.setIcon(ImageProvider.get("dialogs", "up")); 227 else if (name.equals("down")) 228 b.setIcon(ImageProvider.get("dialogs", "down")); 229 else 230 b.setText(name); 231 b.addActionListener(moveAction); 232 b.setActionCommand(name); 233 return b; 234 } 235 236 public void ok() { 237 StringBuilder b = new StringBuilder(); 238 for (int i = 0; i < selected.size(); ++i) { 239 if (selected.get(i) == null) 240 b.append("|"); 241 else 242 b.append(((Action)selected.get(i)).getValue("toolbar")); 243 b.append(";"); 244 } 245 String s = b.toString(); 246 if (s.length() > 0) 247 s = s.substring(0, s.length()-1); 248 else 249 s = "null"; 250 Main.pref.put("toolbar", s); 251 refreshToolbarControl(); 252 } 253 254 /** 255 * @return The parameter (for better chaining) 256 */ 257 public Action register(Action action) { 258 actions.put((String)action.getValue("toolbar"), action); 259 return action; 260 } 261 262 /** 263 * Parse the toolbar preference setting and construct the toolbar GUI control. 264 * 265 * Call this, if anything has changed in the toolbar settings and you want to refresh 266 * the toolbar content (e.g. after registering actions in a plugin) 267 */ 268 public void refreshToolbarControl() { 269 control.removeAll(); 270 for (String s : getToolString()) { 271 if (s.equals("|")) 272 control.addSeparator(); 273 else 274 control.add(actions.get(s)); 275 } 276 control.setVisible(control.getComponentCount() != 0); 277 } 278 278 } -
trunk/src/org/openstreetmap/josm/gui/preferences/prefJPanel.java
r1084 r1169 29 29 public class prefJPanel extends javax.swing.JPanel { 30 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 31 // table of shortcuts 32 private TableModel model; 33 // comboboxes of modifier groups, mapping selectedIndex to real data 34 private static int[] modifInts = new int[]{ 35 -1, 36 0, 37 KeyEvent.SHIFT_DOWN_MASK, 38 KeyEvent.CTRL_DOWN_MASK, 39 KeyEvent.ALT_DOWN_MASK, 40 KeyEvent.META_DOWN_MASK, 41 KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK, 42 KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK, 43 KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK, 44 KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK, 45 KeyEvent.CTRL_DOWN_MASK | KeyEvent.META_DOWN_MASK, 46 KeyEvent.ALT_DOWN_MASK | KeyEvent.META_DOWN_MASK, 47 KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK, 48 KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK 49 }; 50 // and here are the texts fro the comboboxes 51 private static String[] modifList = new String[] { 52 tr("disabled"), 53 tr("no modifier"), 54 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[2]).getModifiers()), 55 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[3]).getModifiers()), 56 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[4]).getModifiers()), 57 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[5]).getModifiers()), 58 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[6]).getModifiers()), 59 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[7]).getModifiers()), 60 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[8]).getModifiers()), 61 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[9]).getModifiers()), 62 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[10]).getModifiers()), 63 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[11]).getModifiers()), 64 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[12]).getModifiers()), 65 KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, modifInts[13]).getModifiers()) 66 }; 67 // this are the display(!) texts for the checkboxes. Let the JVM do the i18n for us <g>. 68 // Ok, there's a real reason for this: The JVM should know best how the keys are labelled 69 // on the physical keyboard. What language pack is installed in JOSM is completely 70 // independent from the keyboard's labelling. But the operation system's locale 71 // usually matches the keyboard. This even works with my English Windows and my German 72 // keyboard. 73 private static String SHIFT = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.SHIFT_DOWN_MASK).getModifiers()); 74 private static String CTRL = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK).getModifiers()); 75 private static String ALT = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.ALT_DOWN_MASK).getModifiers()); 76 private static String META = KeyEvent.getKeyModifiersText(javax.swing.KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK).getModifiers()); 77 78 // A list of keys to present the user. Sadly this really is a list of keys Java knows about, 79 // not a list of real physical keys. If someone knows how to get that list? 80 private static Map<Integer, String> keyList = setKeyList(); 81 82 private static Map<Integer, String> setKeyList() { 83 Map<Integer, String> list = new LinkedHashMap<Integer, String>(); 84 // I hate this, but I found no alternative... 85 for (int i = 0; i < 65534; i++) { 86 String s = KeyEvent.getKeyText(i); 87 if (s != null && s.length() > 0 && !s.contains("Unknown")) { 88 list.put(new Integer(i), s); 89 //System.out.println(i+": "+s); 90 } 91 } 92 list.put(new Integer(-1), ""); 93 return list; 94 } 95 95 96 96 /** Creates new form prefJPanel */ 97 97 // Ain't those auto-generated comments helpful or what? <g> 98 98 public prefJPanel(TableModel model) { 99 99 this.model = model; 100 100 initComponents(); 101 101 } … … 156 156 setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS)); 157 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 158 // If someone wants to move this text into some resource, feel free. 159 infoTab.setLayout(new javax.swing.BoxLayout(shortcutTab, javax.swing.BoxLayout.Y_AXIS)); 160 JEditorPane editor = new JEditorPane(); 161 editor.setEditable(false); 162 editor.setContentType("text/html"); 163 editor.setText( 164 tr("<h1><a name=\"top\">Keyboard Shortcuts</a></h1>")+ 165 tr("<p>Please note that shortcuts keys are assigned to the actions when JOSM is started. So you need to <b>restart</b> " 166 +"JOSM to see your changes.</p>")+ 167 tr("<p>Furthermore, the shortcuts are activated when the actions are assigned to a menu entry of button for the first " 168 +"time. So some of your changes may become active even without restart --- but also without collistion handling. " 169 +"This is another reason to <b>restart</b> JOSM after making any changes here.</p>")+ 170 tr("<p>You may notice that the key selection list on the next page lists all keys that exist on all kinds of keyboards " 171 +"Java knows about, not just those keys that exist on your keyboard. Please use only those values that correspond to " 172 +"a real key on your keyboard. So if your keyboard has no 'Copy' key (PC keyboard don't have them, Sun keyboards do), " 173 +"the do not use it. Also there will be 'keys' listed that correspond to a shortcut on your keyboard (e.g. ':'/Colon). " 174 +"Please also do not use them, use the base key (';'/Semicolon on US keyboards, '.'/Period on German keyboards, ...) " 175 +"instead. Not doing so may result in conflicts, as there is no way for JOSM to know that Ctrl+Shift+; and Ctrl+: " 176 +"actually is the same thing on an US keyboard...</p>")+ 177 tr("<p>Thank you for your understanding</p>")+ 178 tr("<h1>Modifier Groups</h1>")+ 179 tr("<p>The last page lists the modifier keys JOSM will automatically assign to shortcuts. For every of the four kinds " 180 +"of shortcuts there are three alternatives. JOSM will try those alternative in the listed order when managing a " 181 +"conflict. If all alternatives would result in shortcuts that are already taken, it will assign a random shortcut " 182 +"instead.</p>")+ 183 tr("<p>The pseudo-modifier 'disabled' will disable the shortcut when encountered.</p>") 184 ); 185 editor.setCaretPosition(0); // scroll up 186 prefTabPane.addTab(tr("Read First"), new JScrollPane(editor)); 187 187 188 188 shortcutTab.setLayout(new javax.swing.BoxLayout(shortcutTab, javax.swing.BoxLayout.Y_AXIS)); … … 342 342 subwindowGroupPane.add(bxTer4); 343 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 344 initbx(); 345 bxPrim1.setAction(action2); 346 bxSec1.setAction(action2); 347 bxTer1.setAction(action2); 348 bxPrim2.setAction(action2); 349 bxSec2.setAction(action2); 350 bxTer2.setAction(action2); 351 bxPrim3.setAction(action2); 352 bxSec3.setAction(action2); 353 bxTer3.setAction(action2); 354 bxPrim4.setAction(action2); 355 bxSec4.setAction(action2); 356 bxTer4.setAction(action2); 357 358 modifierTab.add(subwindowGroupPane); 359 359 360 360 prefTabPane.addTab(tr("Modifier Groups"), modifierScroller); … … 363 363 } 364 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 365 // this allows to edit shortcuts. it: 366 // * sets the edit controls to the selected shortcut 367 // * enabled/disables the controls as needed 368 // * writes the user's changes to the shortcut 369 // And after I finally had it working, I realized that those two methods 370 // are playing ping-pong (politically correct: table tennis, I know) and 371 // even have some duplicated code. Feel free to refactor, If you have 372 // more expirience with GUI coding than I have. 373 private class cbAction extends javax.swing.AbstractAction implements ListSelectionListener { 374 private prefJPanel panel; 375 public cbAction (prefJPanel panel) { 376 this.panel = panel; 377 } 378 public void valueChanged(ListSelectionEvent e) { 379 ListSelectionModel lsm = panel.shortcutTable.getSelectionModel(); // can't use e here 380 if (!lsm.isSelectionEmpty()) { 381 int row = lsm.getMinSelectionIndex(); 382 Shortcut sc = (Shortcut)panel.model.getValueAt(row, -1); 383 panel.cbDefault.setSelected(!sc.getAssignedUser()); 384 panel.cbDisable.setSelected(sc.getKeyStroke() == null); 385 panel.cbShift.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.SHIFT_DOWN_MASK) != 0); 386 panel.cbCtrl.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.CTRL_DOWN_MASK) != 0); 387 panel.cbAlt.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.ALT_DOWN_MASK) != 0); 388 panel.cbMeta.setSelected(sc.getAssignedModifier() != -1 && (sc.getAssignedModifier() & KeyEvent.META_DOWN_MASK) != 0); 389 if (sc.getKeyStroke() != null) { 390 tfKey.setSelectedItem(keyList.get(sc.getKeyStroke().getKeyCode())); 391 } else { 392 tfKey.setSelectedItem(keyList.get(-1)); 393 } 394 if (sc.getAutomatic()) { 395 panel.cbDefault.setEnabled(false); 396 panel.cbDisable.setEnabled(false); 397 panel.cbShift.setEnabled(false); 398 panel.cbCtrl.setEnabled(false); 399 panel.cbAlt.setEnabled(false); 400 panel.cbMeta.setEnabled(false); 401 panel.tfKey.setEnabled(false); 402 } else { 403 panel.cbDefault.setEnabled(true); 404 actionPerformed(null); 405 } 406 } else { 407 panel.cbDefault.setEnabled(false); 408 panel.cbDisable.setEnabled(false); 409 panel.cbShift.setEnabled(false); 410 panel.cbCtrl.setEnabled(false); 411 panel.cbAlt.setEnabled(false); 412 panel.cbMeta.setEnabled(false); 413 panel.tfKey.setEnabled(false); 414 } 415 } 416 public void actionPerformed(java.awt.event.ActionEvent e) { 417 ListSelectionModel lsm = panel.shortcutTable.getSelectionModel(); 418 if (lsm != null && !lsm.isSelectionEmpty()) { 419 if (e != null) { // only if we've been called by a user action 420 int row = lsm.getMinSelectionIndex(); 421 Shortcut sc = (Shortcut)panel.model.getValueAt(row, -1); 422 sc.setAssignedUser(!panel.cbDefault.isSelected()); 423 if (panel.cbDisable.isSelected()) { 424 sc.setAssignedModifier(-1); 425 } else if (panel.tfKey.getSelectedItem().equals("")) { 426 sc.setAssignedModifier(KeyEvent.VK_CANCEL); 427 } else { 428 sc.setAssignedModifier( 429 (panel.cbShift.isSelected() ? KeyEvent.SHIFT_DOWN_MASK : 0) | 430 (panel.cbCtrl.isSelected() ? KeyEvent.CTRL_DOWN_MASK : 0) | 431 (panel.cbAlt.isSelected() ? KeyEvent.ALT_DOWN_MASK : 0) | 432 (panel.cbMeta.isSelected() ? KeyEvent.META_DOWN_MASK : 0) 433 ); 434 for (Map.Entry<Integer, String> entry : keyList.entrySet()) { 435 if (entry.getValue().equals(panel.tfKey.getSelectedItem())) { 436 sc.setAssignedKey(entry.getKey()); 437 } 438 } 439 } 440 valueChanged(null); 441 } 442 boolean state = !panel.cbDefault.isSelected(); 443 panel.cbDisable.setEnabled(state); 444 state = state && !panel.cbDisable.isSelected(); 445 panel.cbShift.setEnabled(state); 446 panel.cbCtrl.setEnabled(state); 447 panel.cbAlt.setEnabled(state); 448 panel.cbMeta.setEnabled(state); 449 panel.tfKey.setEnabled(state); 450 } else { 451 panel.cbDefault.setEnabled(false); 452 panel.cbDisable.setEnabled(false); 453 panel.cbShift.setEnabled(false); 454 panel.cbCtrl.setEnabled(false); 455 panel.cbAlt.setEnabled(false); 456 panel.cbMeta.setEnabled(false); 457 panel.tfKey.setEnabled(false); 458 } 459 } 460 } 461 462 // this handles the modifier groups 463 private class bxAction extends javax.swing.AbstractAction { 464 public void actionPerformed(java.awt.event.ActionEvent e) { 465 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT), Integer.toString( modifInts[bxPrim1.getSelectedIndex()] )); 466 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_EDIT), Integer.toString( modifInts[ bxSec1.getSelectedIndex()] )); 467 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_EDIT), Integer.toString( modifInts[ bxTer1.getSelectedIndex()] )); 468 469 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU), Integer.toString( modifInts[bxPrim2.getSelectedIndex()] )); 470 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_MENU), Integer.toString( modifInts[ bxSec2.getSelectedIndex()] )); 471 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_MENU), Integer.toString( modifInts[ bxTer2.getSelectedIndex()] )); 472 473 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY), Integer.toString( modifInts[bxPrim3.getSelectedIndex()] )); 474 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_HOTKEY), Integer.toString( modifInts[ bxSec3.getSelectedIndex()] )); 475 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_HOTKEY), Integer.toString( modifInts[ bxTer3.getSelectedIndex()] )); 476 477 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER), Integer.toString( modifInts[bxPrim4.getSelectedIndex()] )); 478 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_LAYER), Integer.toString( modifInts[ bxSec4.getSelectedIndex()] )); 479 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_LAYER), Integer.toString( modifInts[ bxTer4.getSelectedIndex()] )); 480 } 481 } 482 483 private void initbx() { 484 setBx(bxPrim1, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT)); 485 setBx(bxSec1, "shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_EDIT)); 486 setBx(bxTer1, "shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_EDIT)); 487 488 setBx(bxPrim2, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU)); 489 setBx(bxSec2, "shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_MENU)); 490 setBx(bxTer2, "shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_MENU)); 491 492 setBx(bxPrim3, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY)); 493 setBx(bxSec3, "shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_HOTKEY)); 494 setBx(bxTer3, "shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_HOTKEY)); 495 496 setBx(bxPrim4, "shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER)); 497 setBx(bxSec4, "shortcut.groups."+(Shortcut.GROUPS_ALT1 +Shortcut.GROUP_LAYER)); 498 setBx(bxTer4, "shortcut.groups."+(Shortcut.GROUPS_ALT2 +Shortcut.GROUP_LAYER)); 499 } 500 private void setBx(javax.swing.JComboBox bx, String key) { 501 int target = Main.pref.getInteger(key, -1); 502 for (int i = 0; i < modifInts.length; i++) { 503 if (modifInts[i] == target) { 504 bx.setSelectedIndex(i); 505 } 506 } 507 } 508 508 509 509 private javax.swing.JComboBox bxPrim1; -
trunk/src/org/openstreetmap/josm/gui/tagging/ForwardActionListener.java
r627 r1169 13 13 */ 14 14 public final class ForwardActionListener implements ActionListener { 15 15 public final TaggingPreset preset; 16 16 17 17 private final PropertiesDialog propertiesDialog; 18 18 19 20 21 22 19 public ForwardActionListener(PropertiesDialog propertiesDialog, TaggingPreset preset) { 20 this.propertiesDialog = propertiesDialog; 21 this.preset = preset; 22 } 23 23 24 25 26 27 28 24 public void actionPerformed(ActionEvent e) { 25 this.propertiesDialog.taggingPresets.setSelectedIndex(0); 26 e.setSource(this); 27 preset.actionPerformed(e); 28 } 29 29 } -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingCellRenderer.java
r627 r1169 15 15 16 16 final public class TaggingCellRenderer extends DefaultListCellRenderer { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 17 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 18 TaggingPreset a = null; 19 if (value instanceof ForwardActionListener) 20 a = ((ForwardActionListener)value).preset; 21 else if (value instanceof TaggingPreset) 22 a = (TaggingPreset)value; 23 String name = a == null ? null : (String)a.getValue(Action.NAME); 24 if (name == null) 25 return super.getListCellRendererComponent(list, "", index, false, false); 26 JComponent c = (JComponent)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 27 JLabel l = new JLabel(name); 28 l.setForeground(c.getForeground()); 29 l.setBackground(c.getBackground()); 30 l.setFont(c.getFont()); 31 l.setBorder(c.getBorder()); 32 ImageIcon icon = (ImageIcon)a.getValue(Action.SMALL_ICON); 33 if (icon != null) 34 l.setIcon(new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH))); 35 else { 36 if (a.types == null) 37 l.setIcon(ImageProvider.get("data", "empty")); 38 else if (a.types.size() != 1) 39 l.setIcon(ImageProvider.get("data", "object")); 40 else 41 l.setIcon(ImageProvider.get("data", a.types.iterator().next().getSimpleName().toLowerCase())); 42 } 43 l.setOpaque(true); 44 return l; 45 } 46 46 } -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java
r1142 r1169 50 50 * read in all predefined presets, either shipped with JOSM or that are 51 51 * in the config directory. 52 * 52 * 53 53 * It is also able to construct dialogs out of preset definitions. 54 54 */ 55 55 public class TaggingPreset extends AbstractAction { 56 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 String v = (value instanceof JComboBox) ? 163 ((JComboBox)value).getEditor().getItem().toString() : 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 // all selected objects share the same value which is either true or false or unset, 199 200 initialState = OsmUtils.trueval.equals(oneValue) ? 201 202 OsmUtils.falseval.equals(oneValue) ? 203 204 205 check = new QuadStateCheckBox(locale_text, initialState, 206 new QuadStateCheckBox.State[] { 207 208 209 210 211 212 213 214 215 check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL, 216 new QuadStateCheckBox.State[] { 217 218 219 220 221 222 223 224 225 226 227 228 229 230 cmds.add(new ChangePropertyCommand(sel, key, 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 * 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 57 public TaggingPresetMenu group = null; 58 public String name; 59 60 public static abstract class Item { 61 public boolean focus = false; 62 abstract void addToPanel(JPanel p, Collection<OsmPrimitive> sel); 63 abstract void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds); 64 boolean requestFocusInWindow() {return false;} 65 } 66 67 public static class Usage { 68 Set<String> values; 69 Boolean hadKeys = false; 70 Boolean hadEmpty = false; 71 public Boolean allSimilar() 72 { 73 return values.size() == 1 && !hadEmpty; 74 } 75 public Boolean unused() 76 { 77 return values.size() == 0; 78 } 79 public String getFirst() 80 { 81 return (String)(values.toArray()[0]); 82 } 83 public Boolean hadKeys() 84 { 85 return hadKeys; 86 } 87 } 88 89 public static final String DIFFERENT = tr("<different>"); 90 91 static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) { 92 Usage returnValue = new Usage(); 93 returnValue.values = new HashSet<String>(); 94 for (OsmPrimitive s : sel) { 95 String v = s.get(key); 96 if (v != null) 97 returnValue.values.add(v); 98 else 99 returnValue.hadEmpty = true; 100 if(s.keys != null && s.keys.size() > 0) 101 returnValue.hadKeys = true; 102 } 103 return returnValue; 104 } 105 106 static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) { 107 108 Usage returnValue = new Usage(); 109 returnValue.values = new HashSet<String>(); 110 for (OsmPrimitive s : sel) { 111 returnValue.values.add(OsmUtils.getNamedOsmBoolean(s.get(key))); 112 } 113 return returnValue; 114 } 115 116 public static class Text extends Item { 117 118 public String key; 119 public String text; 120 public String locale_text; 121 public String default_; 122 public String originalValue; 123 public boolean use_last_as_default = false; 124 public boolean delete_if_empty = false; 125 126 private JComponent value; 127 128 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 129 130 // find out if our key is already used in the selection. 131 Usage usage = determineTextUsage(sel, key); 132 if (usage.unused()) 133 { 134 value = new JTextField(); 135 if (use_last_as_default && lastValue.containsKey(key)) { 136 ((JTextField)value).setText(lastValue.get(key)); 137 } else { 138 ((JTextField)value).setText(default_); 139 } 140 originalValue = null; 141 } else if (usage.allSimilar()) { 142 // all objects use the same value 143 value = new JTextField(); 144 for (String s : usage.values) ((JTextField) value).setText(s); 145 originalValue = ((JTextField)value).getText(); 146 } else { 147 // the objects have different values 148 value = new JComboBox(usage.values.toArray()); 149 ((JComboBox)value).setEditable(true); 150 ((JComboBox)value).getEditor().setItem(DIFFERENT); 151 originalValue = DIFFERENT; 152 } 153 if(locale_text == null) 154 locale_text = tr(text); 155 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0)); 156 p.add(value, GBC.eol().fill(GBC.HORIZONTAL)); 157 } 158 159 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) { 160 161 // return if unchanged 162 String v = (value instanceof JComboBox) ? 163 ((JComboBox)value).getEditor().getItem().toString() : 164 ((JTextField)value).getText(); 165 166 if (use_last_as_default) lastValue.put(key, v); 167 if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) return; 168 169 if (delete_if_empty && v.length() == 0) 170 v = null; 171 cmds.add(new ChangePropertyCommand(sel, key, v)); 172 } 173 @Override boolean requestFocusInWindow() {return value.requestFocusInWindow();} 174 } 175 176 public static class Check extends Item { 177 178 public String key; 179 public String text; 180 public String locale_text; 181 public boolean default_ = false; // not used! 182 public boolean use_last_as_default = false; 183 184 private QuadStateCheckBox check; 185 private QuadStateCheckBox.State initialState; 186 187 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 188 189 // find out if our key is already used in the selection. 190 Usage usage = determineBooleanUsage(sel, key); 191 192 if(locale_text == null) 193 locale_text = tr(text); 194 195 String oneValue = null; 196 for (String s : usage.values) oneValue = s; 197 if (usage.values.size() < 2 && (oneValue == null || OsmUtils.trueval.equals(oneValue) || OsmUtils.falseval.equals(oneValue))) { 198 // all selected objects share the same value which is either true or false or unset, 199 // we can display a standard check box. 200 initialState = OsmUtils.trueval.equals(oneValue) ? 201 QuadStateCheckBox.State.SELECTED : 202 OsmUtils.falseval.equals(oneValue) ? 203 QuadStateCheckBox.State.NOT_SELECTED : 204 QuadStateCheckBox.State.UNSET; 205 check = new QuadStateCheckBox(locale_text, initialState, 206 new QuadStateCheckBox.State[] { 207 QuadStateCheckBox.State.SELECTED, 208 QuadStateCheckBox.State.NOT_SELECTED, 209 QuadStateCheckBox.State.UNSET }); 210 } else { 211 // the objects have different values, or one or more objects have something 212 // else than true/false. we display a quad-state check box 213 // in "partial" state. 214 initialState = QuadStateCheckBox.State.PARTIAL; 215 check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL, 216 new QuadStateCheckBox.State[] { 217 QuadStateCheckBox.State.PARTIAL, 218 QuadStateCheckBox.State.SELECTED, 219 QuadStateCheckBox.State.NOT_SELECTED, 220 QuadStateCheckBox.State.UNSET }); 221 } 222 p.add(check, GBC.eol().fill(GBC.HORIZONTAL)); 223 } 224 225 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) { 226 // if the user hasn't changed anything, don't create a command. 227 if (check.getState() == initialState) return; 228 229 // otherwise change things according to the selected value. 230 cmds.add(new ChangePropertyCommand(sel, key, 231 check.getState() == QuadStateCheckBox.State.SELECTED ? OsmUtils.trueval : 232 check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? OsmUtils.falseval : 233 null)); 234 } 235 @Override boolean requestFocusInWindow() {return check.requestFocusInWindow();} 236 } 237 238 public static class Combo extends Item { 239 240 public String key; 241 public String text; 242 public String locale_text; 243 public String values; 244 public String display_values; 245 public String locale_display_values; 246 public String default_; 247 public boolean delete_if_empty = false; 248 public boolean editable = true; 249 public boolean use_last_as_default = false; 250 251 private JComboBox combo; 252 private LinkedHashMap<String,String> lhm; 253 private Usage usage; 254 private String originalValue; 255 256 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 257 258 // find out if our key is already used in the selection. 259 usage = determineTextUsage(sel, key); 260 261 String[] value_array = values.split(","); 262 String[] display_array; 263 if(locale_display_values != null) 264 display_array = locale_display_values.split(","); 265 else if(display_values != null) 266 display_array = display_values.split(","); 267 else 268 display_array = value_array; 269 270 lhm = new LinkedHashMap<String,String>(); 271 if (!usage.allSimilar() && !usage.unused()) 272 { 273 lhm.put(DIFFERENT, DIFFERENT); 274 } 275 for (int i=0; i<value_array.length; i++) { 276 lhm.put(value_array[i], 277 (locale_display_values == null) ? 278 tr(display_array[i]) : display_array[i]); 279 } 280 if(!usage.unused()) 281 { 282 for (String s : usage.values) { 283 if (!lhm.containsKey(s)) lhm.put(s, s); 284 } 285 } 286 if (default_ != null && !lhm.containsKey(default_)) lhm.put(default_, default_); 287 if(!lhm.containsKey("")) lhm.put("", ""); 288 289 combo = new JComboBox(lhm.values().toArray()); 290 combo.setEditable(editable); 291 if (usage.allSimilar() && !usage.unused()) 292 { 293 originalValue=usage.getFirst(); 294 combo.setSelectedItem(lhm.get(originalValue)); 295 } 296 // use default only in case it is a totally new entry 297 else if(default_ != null && !usage.hadKeys()) 298 { 299 combo.setSelectedItem(default_); 300 originalValue=DIFFERENT; 301 } 302 else if(usage.unused()) 303 { 304 combo.setSelectedItem(""); 305 originalValue=""; 306 } 307 else 308 { 309 combo.setSelectedItem(DIFFERENT); 310 originalValue=DIFFERENT; 311 } 312 313 if(locale_text == null) 314 locale_text = tr(text); 315 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0)); 316 p.add(combo, GBC.eol().fill(GBC.HORIZONTAL)); 317 } 318 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) { 319 Object obj = combo.getSelectedItem(); 320 String display = (obj == null) ? null : obj.toString(); 321 String value = null; 322 if(display == null && combo.isEditable()) 323 display = combo.getEditor().getItem().toString(); 324 325 if (display != null) 326 { 327 for (String key : lhm.keySet()) { 328 String k = lhm.get(key); 329 if (k != null && k.equals(display)) value=key; 330 } 331 if(value == null) 332 value = display; 333 } 334 else 335 value = ""; 336 337 // no change if same as before 338 if (value.equals(originalValue) || (originalValue == null && (value == null || value.length() == 0))) return; 339 340 if (delete_if_empty && value != null && value.length() == 0) 341 value = null; 342 cmds.add(new ChangePropertyCommand(sel, key, value)); 343 } 344 @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();} 345 } 346 347 public static class Label extends Item { 348 public String text; 349 public String locale_text; 350 351 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 352 if(locale_text == null) 353 locale_text = tr(text); 354 p.add(new JLabel(locale_text), GBC.eol()); 355 } 356 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {} 357 } 358 359 public static class Key extends Item { 360 public String key; 361 public String value; 362 363 @Override public void addToPanel(JPanel p, Collection<OsmPrimitive> sel) { } 364 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) { 365 cmds.add(new ChangePropertyCommand(sel, key, value != null && !value.equals("") ? value : null)); 366 } 367 } 368 369 /** 370 * The types as preparsed collection. 371 */ 372 public Collection<Class<?>> types; 373 public List<Item> data = new LinkedList<Item>(); 374 private static HashMap<String,String> lastValue = new HashMap<String,String>(); 375 376 /** 377 * Create an empty tagging preset. This will not have any items and 378 * will be an empty string as text. createPanel will return null. 379 * Use this as default item for "do not select anything". 380 */ 381 public TaggingPreset() {} 382 383 /** 384 * Change the display name without changing the toolbar value. 385 */ 386 public void setDisplayName() { 387 putValue(Action.NAME, getName()); 388 putValue("toolbar", "tagging_" + getRawName()); 389 putValue(SHORT_DESCRIPTION, "<html>"+ (group != null ? 390 tr("Use preset ''{0}'' of group ''{1}''", tr(name), group.getName()) : 391 tr("Use preset ''{0}''", tr(name))) 392 +"</html>"); 393 } 394 395 public String getName() { 396 return group != null ? group.getName() + "/" + tr(name) : tr(name); 397 } 398 public String getRawName() { 399 return group != null ? group.getRawName() + "/" + name : name; 400 } 401 /** 402 * Called from the XML parser to set the icon 403 * 404 * FIXME for Java 1.6 - use 24x24 icons for LARGE_ICON_KEY (button bar) 405 * and the 16x16 icons for SMALL_ICON. 406 */ 407 public void setIcon(String iconName) { 408 String s = Main.pref.get("taggingpreset.iconpaths"); 409 ImageIcon icon = ImageProvider.getIfAvailable((s != null ? s.split(";") : null), "presets", null, iconName); 410 if (icon == null) 411 { 412 System.out.println("Could not get presets icon " + iconName); 413 icon = new ImageIcon(iconName); 414 } 415 if (Math.max(icon.getIconHeight(), icon.getIconWidth()) != 16) 416 icon = new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); 417 putValue(Action.SMALL_ICON, icon); 418 } 419 420 /** 421 * Called from the XML parser to set the types this preset affects 422 */ 423 public void setType(String types) throws SAXException { 424 try { 425 for (String type : types.split(",")) { 426 type = Character.toUpperCase(type.charAt(0))+type.substring(1); 427 if (this.types == null) 428 this.types = new LinkedList<Class<?>>(); 429 this.types.add(Class.forName("org.openstreetmap.josm.data.osm."+type)); 430 } 431 } catch (ClassNotFoundException e) { 432 e.printStackTrace(); 433 throw new SAXException(tr("Unknown type")); 434 } 435 } 436 437 public static List<TaggingPreset> readAll(Reader in) throws SAXException { 438 XmlObjectParser parser = new XmlObjectParser(); 439 parser.mapOnStart("item", TaggingPreset.class); 440 parser.mapOnStart("separator", TaggingPresetSeparator.class); 441 parser.mapBoth("group", TaggingPresetMenu.class); 442 parser.map("text", Text.class); 443 parser.map("check", Check.class); 444 parser.map("combo", Combo.class); 445 parser.map("label", Label.class); 446 parser.map("key", Key.class); 447 LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>(); 448 TaggingPresetMenu lastmenu = null; 449 parser.start(in); 450 while(parser.hasNext()) { 451 Object o = parser.next(); 452 if (o instanceof TaggingPresetMenu) { 453 TaggingPresetMenu tp = (TaggingPresetMenu) o; 454 if(tp == lastmenu) 455 lastmenu = tp.group; 456 else 457 { 458 tp.setDisplayName(); 459 tp.group = lastmenu; 460 lastmenu = tp; 461 all.add(tp); 462 Main.toolbar.register(tp); 463 464 } 465 } else if (o instanceof TaggingPresetSeparator) { 466 TaggingPresetSeparator tp = (TaggingPresetSeparator) o; 467 tp.group = lastmenu; 468 all.add(tp); 469 } else if (o instanceof TaggingPreset) { 470 TaggingPreset tp = (TaggingPreset) o; 471 tp.group = lastmenu; 472 tp.setDisplayName(); 473 all.add(tp); 474 Main.toolbar.register(tp); 475 } else 476 all.getLast().data.add((Item)o); 477 } 478 return all; 479 } 480 481 public static Collection<TaggingPreset> readFromPreferences() { 482 LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>(); 483 String allTaggingPresets = Main.pref.get("taggingpreset.sources"); 484 485 if (Main.pref.getBoolean("taggingpreset.enable-defaults", true)) 486 { 487 allTaggingPresets = "resource://presets/presets.xml" 488 + (allTaggingPresets != null ? ";"+allTaggingPresets : ""); 489 } 490 491 for(String source : allTaggingPresets.split(";")) 492 { 493 try { 494 MirroredInputStream s = new MirroredInputStream(source); 495 InputStreamReader r; 496 try 497 { 498 r = new InputStreamReader(s, "UTF-8"); 499 } 500 catch (UnsupportedEncodingException e) 501 { 502 r = new InputStreamReader(s); 503 } 504 allPresets.addAll(TaggingPreset.readAll(new BufferedReader(r))); 505 } catch (IOException e) { 506 e.printStackTrace(); 507 JOptionPane.showMessageDialog(Main.parent, tr("Could not read tagging preset source: {0}",source)); 508 } catch (SAXException e) { 509 e.printStackTrace(); 510 JOptionPane.showMessageDialog(Main.parent, tr("Error parsing {0}: ", source)+e.getMessage()); 511 } 512 } 513 return allPresets; 514 } 515 516 public JPanel createPanel(Collection<OsmPrimitive> selected) { 517 if (data == null) 518 return null; 519 JPanel p = new JPanel(new GridBagLayout()); 520 521 for (Item i : data) 522 i.addToPanel(p, selected); 523 return p; 524 } 525 526 public void actionPerformed(ActionEvent e) { 527 Collection<OsmPrimitive> sel = Main.ds.getSelected(); 528 JPanel p = createPanel(sel); 529 if (p == null) 530 return; 531 int answer = JOptionPane.OK_OPTION; 532 if (p.getComponentCount() != 0) { 533 final JOptionPane optionPane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){ 534 @Override public void selectInitialValue() { 535 for (Item i : data) { 536 if (i.focus) { 537 i.requestFocusInWindow(); 538 return; 539 } 540 } 541 } 542 }; 543 optionPane.createDialog(Main.parent, trn("Change {0} object", "Change {0} objects", sel.size(), sel.size())).setVisible(true); 544 Object answerObj = optionPane.getValue(); 545 if (answerObj == null || answerObj == JOptionPane.UNINITIALIZED_VALUE || 546 (answerObj instanceof Integer && (Integer)answerObj != JOptionPane.OK_OPTION)) 547 answer = JOptionPane.CANCEL_OPTION; 548 } 549 if (answer == JOptionPane.OK_OPTION) { 550 Command cmd = createCommand(Main.ds.getSelected()); 551 if (cmd != null) 552 Main.main.undoRedo.add(cmd); 553 } 554 Main.ds.setSelected(Main.ds.getSelected()); // force update 555 } 556 557 private Command createCommand(Collection<OsmPrimitive> participants) { 558 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>(); 559 for (OsmPrimitive osm : participants) 560 if (types == null || types.contains(osm.getClass())) 561 sel.add(osm); 562 if (sel.isEmpty()) 563 return null; 564 565 List<Command> cmds = new LinkedList<Command>(); 566 for (Item i : data) 567 i.addCommands(sel, cmds); 568 if (cmds.size() == 0) 569 return null; 570 else if (cmds.size() == 1) 571 return cmds.get(0); 572 else 573 return new SequenceCommand(tr("Change Properties"), cmds); 574 } 575 575 } -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetMenu.java
r878 r1169 16 16 17 17 public class TaggingPresetMenu extends TaggingPreset { 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 18 public JMenu menu = null; // set by TaggingPresetPreferences 19 public void setDisplayName() { 20 String n = getName(); 21 putValue(Action.NAME, n); 22 putValue(SHORT_DESCRIPTION, "<html>"+tr("Preset group ''{0}''", n)+"</html>"); 23 putValue("toolbar", "tagginggroup_" + getRawName()); 24 } 25 public void setIcon(String iconName) { 26 super.setIcon(iconName); 27 } 28 public void actionPerformed(ActionEvent e) { 29 Object s = e.getSource(); 30 if(menu != null && s instanceof Component) 31 { 32 Component co = (Component)s; 33 JPopupMenu pm = new JPopupMenu(getName()); 34 for(Component c : menu.getMenuComponents()) 35 { 36 if(c instanceof JMenuItem) 37 { 38 JMenuItem j = new JMenuItem(((JMenuItem)c).getAction()); 39 j.setText(((JMenuItem)c).getText()); 40 pm.add(j); 41 } 42 else if(c instanceof JSeparator) 43 pm.addSeparator(); 44 } 45 pm.show(co, co.getWidth()/2, co.getHeight()/2); 46 } 47 } 48 48 } -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetSeparator.java
r895 r1169 5 5 6 6 public class TaggingPresetSeparator extends TaggingPreset { 7 7 public void setDisplayName() {} 8 8 } -
trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java
r999 r1169 15 15 public class BoundingBoxDownloader extends OsmServerReader { 16 16 17 18 19 20 21 22 23 17 /** 18 * The boundings of the desired map data. 19 */ 20 private final double lat1; 21 private final double lon1; 22 private final double lat2; 23 private final double lon2; 24 24 25 26 27 28 29 30 31 32 33 25 public BoundingBoxDownloader(double lat1, double lon1, double lat2, double lon2) { 26 this.lat1 = lat1; 27 this.lon1 = lon1; 28 this.lat2 = lat2; 29 this.lon2 = lon2; 30 // store the bounding box in the preferences so it can be 31 // re-used across invocations of josm 32 Main.pref.put("osm-download.bounds", lat1+";"+lon1+";"+lat2+";"+lon2); 33 } 34 34 35 36 37 38 39 40 41 42 43 44 45 35 /** 36 * Retrieve raw gps waypoints from the server API. 37 * @return A list of all primitives retrieved. Currently, the list of lists 38 * contain only one list, since the server cannot distinguish between 39 * ways. 40 */ 41 public GpxData parseRawGps() throws IOException, SAXException { 42 Main.pleaseWaitDlg.progress.setValue(0); 43 Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server...")); 44 try { 45 String url = "trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page="; 46 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 47 boolean done = false; 48 GpxData result = null; 49 for (int i = 0;!done;++i) { 50 Main.pleaseWaitDlg.currentAction.setText(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000))); 51 InputStream in = getInputStream(url+i, Main.pleaseWaitDlg); 52 if (in == null) 53 break; 54 GpxData currentGpx = new GpxReader(in, null).data; 55 if (result == null) { 56 result = currentGpx; 57 } else if (currentGpx.hasTrackPoints()) { 58 result.mergeFrom(currentGpx); 59 } else{ 60 done = true; 61 } 62 in.close(); 63 activeConnection = null; 64 } 65 result.fromServer = true; 66 return result; 67 } catch (IllegalArgumentException e) { 68 // caused by HttpUrlConnection in case of illegal stuff in the response 69 if (cancel) 70 return null; 71 throw new SAXException("Illegal characters within the HTTP-header response", e); 72 } catch (IOException e) { 73 if (cancel) 74 return null; 75 throw e; 76 } catch (SAXException e) { 77 throw e; 78 } catch (Exception e) { 79 if (cancel) 80 return null; 81 if (e instanceof RuntimeException) 82 throw (RuntimeException)e; 83 throw new RuntimeException(e); 84 } 85 } 86 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 87 /** 88 * Read the data from the osm server address. 89 * @return A data set containing all data retrieved from that url 90 */ 91 public DataSet parseOsm() throws SAXException, IOException { 92 try { 93 Main.pleaseWaitDlg.progress.setValue(0); 94 Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server...")); 95 final InputStream in = getInputStream("map?bbox="+lon1+","+lat1+","+lon2+","+lat2, Main.pleaseWaitDlg); 96 if (in == null) 97 return null; 98 Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data...")); 99 final DataSet data = OsmReader.parseDataSet(in, null, Main.pleaseWaitDlg); 100 in.close(); 101 activeConnection = null; 102 return data; 103 } catch (IOException e) { 104 if (cancel) 105 return null; 106 throw e; 107 } catch (SAXException e) { 108 throw e; 109 } catch (Exception e) { 110 if (cancel) 111 return null; 112 if (e instanceof RuntimeException) 113 throw (RuntimeException)e; 114 throw new RuntimeException(e); 115 } 116 } 117 117 } -
trunk/src/org/openstreetmap/josm/io/DiffResultReader.java
r1071 r1169 41 41 */ 42 42 public class DiffResultReader implements Visitor { 43 43 44 44 /** 45 45 * mapping from old id to new id/version … … 47 47 private Map<String, Long[]> versions = new HashMap<String, Long[]>(); 48 48 private Collection<OsmPrimitive> processed; 49 private Map<OsmPrimitive,Long> newIdMap; 49 private Map<OsmPrimitive,Long> newIdMap; 50 50 51 /** 51 /** 52 52 * List of protocol versions that will be accepted on reading 53 53 */ 54 54 55 55 private class Parser extends DefaultHandler { 56 56 … … 81 81 * elemet found there is returned. 82 82 */ 83 public static void parseDiffResult(InputStream source, Collection<OsmPrimitive> osm, Collection<OsmPrimitive> processed, Map<OsmPrimitive,Long> newIdMap, PleaseWaitDialog pleaseWaitDlg) 83 public static void parseDiffResult(InputStream source, Collection<OsmPrimitive> osm, Collection<OsmPrimitive> processed, Map<OsmPrimitive,Long> newIdMap, PleaseWaitDialog pleaseWaitDlg) 84 84 throws SAXException, IOException { 85 85 … … 107 107 } 108 108 } 109 109 110 110 public void visit(Node n) { 111 111 String key = "node:" + (newIdMap.containsKey(n) ? newIdMap.get(n) : n.id); … … 127 127 w.id = nv[0]; w.version = nv[1].intValue(); 128 128 } 129 } 129 } 130 130 } 131 131 public void visit(Relation r) { … … 137 137 r.id = nv[0]; r.version = nv[1].intValue(); 138 138 } 139 } 139 } 140 140 } 141 141 } -
trunk/src/org/openstreetmap/josm/io/GpxReader.java
r1122 r1169 35 35 */ 36 36 public class GpxReader { 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 } 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 currentWayPoint.setTime(); 233 234 235 currentWayPoint.setGarminCommentTime(qName); 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 * 289 * @param relativeMarkerPath The directory to use as relative path for all <wpt> 290 * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers. 291 292 293 294 295 296 297 298 299 300 301 302 303 304 37 // TODO: implement GPX 1.0 parsing 38 39 /** 40 * The resulting gpx data 41 */ 42 public GpxData data; 43 public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg } 44 45 private class Parser extends DefaultHandler { 46 47 private GpxData currentData; 48 private GpxTrack currentTrack; 49 private Collection<WayPoint> currentTrackSeg; 50 private GpxRoute currentRoute; 51 private WayPoint currentWayPoint; 52 53 private state currentState = state.init; 54 55 private GpxLink currentLink; 56 private Stack<state> states; 57 58 private StringBuffer accumulator = new StringBuffer(); 59 60 @Override public void startDocument() { 61 accumulator = new StringBuffer(); 62 states = new Stack<state>(); 63 currentData = new GpxData(); 64 } 65 66 private double parseCoord(String s) { 67 try { 68 return Double.parseDouble(s); 69 } catch (NumberFormatException ex) { 70 return Double.NaN; 71 } 72 } 73 74 private LatLon parseLatLon(Attributes atts) { 75 return new LatLon( 76 parseCoord(atts.getValue("lat")), 77 parseCoord(atts.getValue("lon"))); 78 } 79 80 @Override public void startElement(String namespaceURI, String qName, String rqName, Attributes atts) throws SAXException { 81 switch(currentState) { 82 case init: 83 if (qName.equals("metadata")) { 84 states.push(currentState); 85 currentState = state.metadata; 86 } else if (qName.equals("wpt")) { 87 states.push(currentState); 88 currentState = state.wpt; 89 currentWayPoint = new WayPoint(parseLatLon(atts)); 90 } else if (qName.equals("rte")) { 91 states.push(currentState); 92 currentState = state.rte; 93 currentRoute = new GpxRoute(); 94 } else if (qName.equals("trk")) { 95 states.push(currentState); 96 currentState = state.trk; 97 currentTrack = new GpxTrack(); 98 } else if (qName.equals("extensions")) { 99 states.push(currentState); 100 currentState = state.ext; 101 } 102 break; 103 case author: 104 if (qName.equals("link")) { 105 states.push(currentState); 106 currentState = state.link; 107 currentLink = new GpxLink(atts.getValue("href")); 108 } 109 break; 110 case trk: 111 if (qName.equals("trkseg")) { 112 states.push(currentState); 113 currentState = state.trkseg; 114 currentTrackSeg = new ArrayList<WayPoint>(); 115 } else if (qName.equals("link")) { 116 states.push(currentState); 117 currentState = state.link; 118 currentLink = new GpxLink(atts.getValue("href")); 119 } else if (qName.equals("extensions")) { 120 states.push(currentState); 121 currentState = state.ext; 122 } 123 break; 124 case metadata: 125 if (qName.equals("author")) { 126 states.push(currentState); 127 currentState = state.author; 128 } else if (qName.equals("extensions")) { 129 states.push(currentState); 130 currentState = state.ext; 131 } 132 break; 133 case trkseg: 134 if (qName.equals("trkpt")) { 135 states.push(currentState); 136 currentState = state.wpt; 137 currentWayPoint = new WayPoint(parseLatLon(atts)); 138 } 139 break; 140 case wpt: 141 if (qName.equals("link")) { 142 states.push(currentState); 143 currentState = state.link; 144 currentLink = new GpxLink(atts.getValue("href")); 145 } else if (qName.equals("extensions")) { 146 states.push(currentState); 147 currentState = state.ext; 148 } 149 break; 150 case rte: 151 if (qName.equals("link")) { 152 states.push(currentState); 153 currentState = state.link; 154 currentLink = new GpxLink(atts.getValue("href")); 155 } else if (qName.equals("rtept")) { 156 states.push(currentState); 157 currentState = state.wpt; 158 currentWayPoint = new WayPoint(parseLatLon(atts)); 159 } else if (qName.equals("extensions")) { 160 states.push(currentState); 161 currentState = state.ext; 162 } 163 break; 164 default: 165 } 166 accumulator.setLength(0); 167 } 168 169 @Override public void characters(char[] ch, int start, int length) { 170 accumulator.append(ch, start, length); 171 } 172 173 private Map<String, Object> getAttr() { 174 switch (currentState) { 175 case rte: return currentRoute.attr; 176 case metadata: return currentData.attr; 177 case wpt: return currentWayPoint.attr; 178 case trk: return currentTrack.attr; 179 default: return null; 180 } 181 } 182 183 @Override public void endElement(String namespaceURI, String qName, String rqName) { 184 switch (currentState) { 185 case metadata: 186 if (qName.equals("name") || qName.equals("desc") || 187 qName.equals("time") || qName.equals("keywords")) { 188 currentData.attr.put(qName, accumulator.toString()); 189 } else if (qName.equals("metadata")) { 190 currentState = states.pop(); 191 } 192 //TODO: parse copyright, bounds, extensions 193 break; 194 case author: 195 if (qName.equals("author")) { 196 currentState = states.pop(); 197 } else if (qName.equals("name") || qName.equals("email")) { 198 currentData.attr.put("author" + qName, accumulator.toString()); 199 } else if (qName.equals("link")) { 200 currentData.attr.put("authorlink", currentLink); 201 } 202 break; 203 case link: 204 if (qName.equals("text")) { 205 currentLink.text = accumulator.toString(); 206 } else if (qName.equals("type")) { 207 currentLink.type = accumulator.toString(); 208 } else if (qName.equals("link")) { 209 // <link>URL</link> 210 if (currentLink.uri == null) 211 currentLink.uri = accumulator.toString(); 212 213 currentState = states.pop(); 214 } 215 if (currentState == state.author) { 216 currentData.attr.put("authorlink", currentLink); 217 } else if (currentState != state.link) { 218 Map<String, Object> attr = getAttr(); 219 if (!attr.containsKey("link")) { 220 attr.put("link", new LinkedList<GpxLink>()); 221 } 222 ((Collection<GpxLink>) attr.get("link")).add(currentLink); 223 } 224 break; 225 case wpt: 226 if (qName.equals("ele") || qName.equals("magvar") 227 || qName.equals("geoidheight") || qName.equals("name") 228 || qName.equals("sym") || qName.equals("type")) { 229 currentWayPoint.attr.put(qName, accumulator.toString()); 230 } else if (qName.equals("time")) { 231 currentWayPoint.attr.put(qName, accumulator.toString()); 232 currentWayPoint.setTime(); 233 } else if (qName.equals("cmt") || qName.equals("desc")) { 234 currentWayPoint.attr.put(qName, accumulator.toString()); 235 currentWayPoint.setGarminCommentTime(qName); 236 } else if (qName.equals("rtept")) { 237 currentState = states.pop(); 238 currentRoute.routePoints.add(currentWayPoint); 239 } else if (qName.equals("trkpt")) { 240 currentState = states.pop(); 241 currentTrackSeg.add(currentWayPoint); 242 } else if (qName.equals("wpt")) { 243 currentState = states.pop(); 244 currentData.waypoints.add(currentWayPoint); 245 } 246 break; 247 case trkseg: 248 if (qName.equals("trkseg")) { 249 currentState = states.pop(); 250 currentTrack.trackSegs.add(currentTrackSeg); 251 } 252 break; 253 case trk: 254 if (qName.equals("trk")) { 255 currentState = states.pop(); 256 currentData.tracks.add(currentTrack); 257 } else if (qName.equals("name") || qName.equals("cmt") 258 || qName.equals("desc") || qName.equals("src") 259 || qName.equals("type") || qName.equals("number")) { 260 currentTrack.attr.put(qName, accumulator.toString()); 261 } 262 break; 263 case ext: 264 if (qName.equals("extensions")) { 265 currentState = states.pop(); 266 } 267 break; 268 default: 269 if (qName.equals("wpt")) { 270 currentState = states.pop(); 271 } else if (qName.equals("rte")) { 272 currentState = states.pop(); 273 currentData.routes.add(currentRoute); 274 } 275 } 276 } 277 278 @Override public void endDocument() throws SAXException { 279 if (!states.empty()) { 280 throw new SAXException(tr("Parse error: invalid document structure for gpx document")); 281 } 282 data = currentData; 283 } 284 } 285 286 /** 287 * Parse the input stream and store the result in trackData and markerData 288 * 289 * @param relativeMarkerPath The directory to use as relative path for all <wpt> 290 * marker tags. Maybe <code>null</code>, in which case no relative urls are constructed for the markers. 291 */ 292 public GpxReader(InputStream source, File relativeMarkerPath) throws SAXException, IOException { 293 294 Parser parser = new Parser(); 295 InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8")); 296 try { 297 SAXParserFactory factory = SAXParserFactory.newInstance(); 298 factory.setNamespaceAware(true); 299 factory.newSAXParser().parse(inputSource, parser); 300 } catch (ParserConfigurationException e) { 301 e.printStackTrace(); // broken SAXException chaining 302 throw new SAXException(e); 303 } 304 } 305 305 } -
trunk/src/org/openstreetmap/josm/io/GpxWriter.java
r1164 r1169 97 97 for (WayPoint pnt : data.waypoints) { 98 98 wayPoint(pnt, WAY_POINT); 99 } 100 } 101 99 } 100 } 101 102 102 private void writeRoutes() { 103 103 for (GpxRoute rte : data.routes) { … … 110 110 } 111 111 } 112 112 113 113 private void writeTracks() { 114 114 for (GpxTrack trk : data.tracks) { … … 140 140 indent += " "; 141 141 } 142 142 143 143 private void inline(String tag, String attributes) { 144 144 out.println(indent + "<" + tag + " " + attributes + " />"); … … 155 155 } 156 156 157 /** 157 /** 158 158 * if content not null, open tag, write encoded content, and close tag 159 159 * else do nothing. … … 168 168 } 169 169 170 /** 170 /** 171 171 * output link 172 172 */ … … 180 180 } 181 181 182 /** 182 /** 183 183 * output a point 184 184 */ -
trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java
r999 r1169 20 20 */ 21 21 public class MirroredInputStream extends InputStream { 22 22 InputStream fs = null; 23 23 24 25 26 27 24 public MirroredInputStream(String name) throws IOException 25 { 26 this(name, null, -1L); 27 } 28 28 29 30 31 32 29 public MirroredInputStream(String name, long maxTime) throws IOException 30 { 31 this(name, null, maxTime); 32 } 33 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 34 public MirroredInputStream(String name, String destDir, long maxTime) throws IOException 35 { 36 URL url; 37 File file = null; 38 try 39 { 40 url = new URL(name); 41 if(url.getProtocol().equals("file")) 42 { 43 file = new File(name.substring("file:/".length())); 44 if(!file.exists()) 45 file = new File(name.substring("file://".length())); 46 } 47 else 48 file = checkLocal(url, destDir, maxTime); 49 } 50 catch(java.net.MalformedURLException e) 51 { 52 if(name.startsWith("resource://")) 53 { 54 fs = getClass().getResourceAsStream( 55 name.substring("resource:/".length())); 56 return; 57 } 58 else 59 file = new File(name); 60 } 61 if(file == null) 62 throw new IOException(); 63 fs = new FileInputStream(file); 64 } 65 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 66 private File checkLocal(URL url, String destDir, long maxTime) 67 { 68 String localPath = Main.pref.get("mirror." + url); 69 File file = null; 70 if(localPath != null && localPath.length() > 0) 71 { 72 String[] lp = localPath.split(";"); 73 file = new File(lp[1]); 74 if(maxTime <= 0) 75 maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60); 76 if(System.currentTimeMillis() - Long.parseLong(lp[0]) < maxTime*1000) 77 { 78 if(file.exists()) 79 { 80 return file; 81 } 82 } 83 } 84 if(destDir == null) 85 destDir = Main.pref.getPreferencesDir(); 86 86 87 88 89 87 File destDirFile = new File(destDir); 88 if(!destDirFile.exists() ) 89 destDirFile.mkdirs(); 90 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 91 localPath = "mirror_" + new File(url.getPath()).getName(); 92 destDirFile = new File(destDir, localPath + ".tmp"); 93 BufferedOutputStream bos = null; 94 BufferedInputStream bis = null; 95 try 96 { 97 URLConnection conn = url.openConnection(); 98 conn.setConnectTimeout(5000); 99 bis = new BufferedInputStream(conn.getInputStream()); 100 bos = new BufferedOutputStream( new FileOutputStream(destDirFile)); 101 byte[] buffer = new byte[4096]; 102 int length; 103 while((length = bis.read(buffer)) > -1) 104 bos.write(buffer, 0, length); 105 } 106 catch(IOException ioe) 107 { 108 if(file != null) 109 return file; 110 else 111 return null; 112 } 113 finally 114 { 115 if(bis != null) 116 { 117 try 118 { 119 bis.close(); 120 } 121 catch (IOException e) 122 { 123 e.printStackTrace(); 124 } 125 } 126 if(bos != null) 127 { 128 try 129 { 130 bos.close(); 131 } 132 catch (IOException e) 133 { 134 e.printStackTrace(); 135 } 136 } 137 file = new File(destDir, localPath); 138 destDirFile.renameTo(file); 139 Main.pref.put("mirror." + url, System.currentTimeMillis() + ";" + file); 140 } 141 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 142 return file; 143 } 144 public int available() throws IOException 145 { return fs.available(); } 146 public void close() throws IOException 147 { fs.close(); } 148 public int read() throws IOException 149 { return fs.read(); } 150 public int read(byte[] b) throws IOException 151 { return fs.read(b); } 152 public int read(byte[] b, int off, int len) throws IOException 153 { return fs.read(b,off, len); } 154 public long skip(long n) throws IOException 155 { return fs.skip(n); } 156 156 } -
trunk/src/org/openstreetmap/josm/io/MultiPartFormOutputStream.java
r627 r1169 3 3 4 4 License 5 5 6 6 Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved. 7 Redistribution and use in source and binary forms, with or without modification, 7 Redistribution and use in source and binary forms, with or without modification, 8 8 are permitted provided that the following conditions are met: 9 10 * Redistribution of source code must retain the above copyright notice, this list 9 10 * Redistribution of source code must retain the above copyright notice, this list 11 11 of conditions and the following disclaimer. 12 12 13 * Redistribution in binary form must reproduce the above copyright notice, this 14 list of conditions and the following disclaimer in the documentation and/or other 13 * Redistribution in binary form must reproduce the above copyright notice, this 14 list of conditions and the following disclaimer in the documentation and/or other 15 15 materials provided with the distribution. 16 16 17 18 Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to 19 endorse or promote products derived from this software without specific prior written 17 18 Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to 19 endorse or promote products derived from this software without specific prior written 20 20 permission. 21 22 This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED 23 CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 24 FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, 25 INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS 26 A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT 27 WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 28 INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 29 REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS 21 22 This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED 23 CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 24 FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, 25 INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS 26 A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT 27 WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 28 INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 29 REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS 30 30 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 31 32 You acknowledge that this software is not designed, licensed or intended for use in the 33 design, construction, operation or maintenance of any nuclear facility. 31 32 You acknowledge that this software is not designed, licensed or intended for use in the 33 design, construction, operation or maintenance of any nuclear facility. 34 34 */ 35 35 … … 44 44 import java.net.URLConnection; 45 45 46 46 47 47 /** 48 * <code>MultiPartFormOutputStream</code> is used to write 49 * "multipart/form-data" to a <code>java.net.URLConnection</code> for 50 * POSTing. This is primarily for file uploading to HTTP servers. 51 * 48 * <code>MultiPartFormOutputStream</code> is used to write 49 * "multipart/form-data" to a <code>java.net.URLConnection</code> for 50 * POSTing. This is primarily for file uploading to HTTP servers. 51 * 52 52 * @since JDK1.3 53 53 */ 54 54 public class MultiPartFormOutputStream extends OsmConnection { 55 56 * The line end characters. 57 58 59 60 61 * The boundary prefix. 62 63 64 65 66 * The output stream to write to. 67 68 69 70 71 * The multipart boundary string. 72 73 74 75 76 * Creates a new <code>MultiPartFormOutputStream</code> object using 77 * the specified output stream and boundary. The boundary is required 78 * to be created before using this method, as described in the 79 * description for the <code>getContentType(String)</code> method. 80 * The boundary is only checked for <code>null</code> or empty string, 81 * but it is recommended to be at least 6 characters. (Or use the 82 83 * 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 * Writes an boolean field value. 103 * 104 105 106 107 108 public void writeField(String name, boolean value) 109 110 111 112 113 114 * Writes an double field value. 115 * 116 117 118 119 120 public void writeField(String name, double value) 121 122 123 124 125 126 * Writes an float field value. 127 * 128 129 130 131 132 public void writeField(String name, float value) 133 134 135 136 137 138 * Writes an long field value. 139 * 140 141 142 143 144 public void writeField(String name, long value) 145 146 147 148 149 150 * Writes an int field value. 151 * 152 153 154 155 156 public void writeField(String name, int value) 157 158 159 160 161 162 * Writes an short field value. 163 * 164 165 166 167 168 public void writeField(String name, short value) 169 170 171 172 173 174 * Writes an char field value. 175 * 176 177 178 179 180 public void writeField(String name, char value) 181 182 183 184 185 186 * Writes an string field value. If the value is null, an empty string 187 * is sent (""). 188 * 189 190 191 192 193 public void writeField(String name, String value) 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 * Writes a file's contents. If the file is null, does not exists, or 223 * is a directory, a <code>java.lang.IllegalArgumentException</code> 224 * will be thrown. 225 * 226 227 228 229 230 231 public void writeFile(String name, String mimeType, java.io.File file) 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 * Writes a input stream's contents. If the input stream is null, a 247 * <code>java.lang.IllegalArgumentException</code> will be thrown. 248 * 249 250 251 252 253 254 255 public void writeFile(String name, String mimeType, 256 String fileName, InputStream is) 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 out.writeBytes("Content-Disposition: form-data; name=\"" + name + 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 * Writes the given bytes. The bytes are assumed to be the contents 300 * of a file, and will be sent as such. If the data is null, a 301 * <code>java.lang.IllegalArgumentException</code> will be thrown. 302 * 303 304 305 306 307 308 309 public void writeFile(String name, String mimeType, 310 String fileName, byte[] data) 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 out.writeBytes("Content-Disposition: form-data; name=\"" + name + 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 * Flushes the stream. Actually, this method does nothing, as the only 346 * write methods are highly specialized and automatically flush. 347 348 349 350 351 352 353 354 355 * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the 356 357 * 358 359 360 361 362 363 364 365 366 367 368 369 370 371 * Gets the multipart boundary string being used by this stream. 372 * 373 374 375 376 377 378 379 380 * Creates a new <code>java.net.URLConnection</code> object from the 381 * specified <code>java.net.URL</code>. This is a convenience method 382 * which will set the <code>doInput</code>, <code>doOutput</code>, 383 * <code>useCaches</code> and <code>defaultUseCaches</code> fields to 384 * the appropriate settings in the correct order. 385 * 386 387 388 389 public static URLConnection createConnection(URL url) 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 * Creates a multipart boundary string by concatenating 20 hyphens (-) 405 * and the hexadecimal (base-16) representation of the current time in 406 * milliseconds. 407 * 408 409 410 411 412 return "--------------------" + 413 414 415 416 417 * Gets the content type string suitable for the 418 * <code>java.net.URLConnection</code> which includes the multipart 419 420 421 * This method is static because, due to the nature of the 422 * <code>java.net.URLConnection</code> class, once the output stream 423 * for the connection is acquired, it's too late to set the content 424 * type (or any other request parameter). So one has to create a 425 * multipart boundary string first before using this class, such as 426 * with the <code>createBoundary()</code> method. 427 * 428 429 430 431 432 433 434 55 /** 56 * The line end characters. 57 */ 58 private static final String NEWLINE = "\r\n"; 59 60 /** 61 * The boundary prefix. 62 */ 63 private static final String PREFIX = "--"; 64 65 /** 66 * The output stream to write to. 67 */ 68 private DataOutputStream out = null; 69 70 /** 71 * The multipart boundary string. 72 */ 73 private String boundary = null; 74 75 /** 76 * Creates a new <code>MultiPartFormOutputStream</code> object using 77 * the specified output stream and boundary. The boundary is required 78 * to be created before using this method, as described in the 79 * description for the <code>getContentType(String)</code> method. 80 * The boundary is only checked for <code>null</code> or empty string, 81 * but it is recommended to be at least 6 characters. (Or use the 82 * static createBoundary() method to create one.) 83 * 84 * @param os the output stream 85 * @param boundary the boundary 86 * @see #createBoundary() 87 * @see #getContentType(String) 88 */ 89 public MultiPartFormOutputStream(OutputStream os, String boundary) { 90 if(os == null) { 91 throw new IllegalArgumentException("Output stream is required."); 92 } 93 if(boundary == null || boundary.length() == 0) { 94 throw new IllegalArgumentException("Boundary stream is required."); 95 } 96 this.out = new DataOutputStream(os); 97 this.boundary = boundary; 98 initAuthentication(); 99 } 100 101 /** 102 * Writes an boolean field value. 103 * 104 * @param name the field name (required) 105 * @param value the field value 106 * @throws java.io.IOException on input/output errors 107 */ 108 public void writeField(String name, boolean value) 109 throws java.io.IOException { 110 writeField(name, new Boolean(value).toString()); 111 } 112 113 /** 114 * Writes an double field value. 115 * 116 * @param name the field name (required) 117 * @param value the field value 118 * @throws java.io.IOException on input/output errors 119 */ 120 public void writeField(String name, double value) 121 throws java.io.IOException { 122 writeField(name, Double.toString(value)); 123 } 124 125 /** 126 * Writes an float field value. 127 * 128 * @param name the field name (required) 129 * @param value the field value 130 * @throws java.io.IOException on input/output errors 131 */ 132 public void writeField(String name, float value) 133 throws java.io.IOException { 134 writeField(name, Float.toString(value)); 135 } 136 137 /** 138 * Writes an long field value. 139 * 140 * @param name the field name (required) 141 * @param value the field value 142 * @throws java.io.IOException on input/output errors 143 */ 144 public void writeField(String name, long value) 145 throws java.io.IOException { 146 writeField(name, Long.toString(value)); 147 } 148 149 /** 150 * Writes an int field value. 151 * 152 * @param name the field name (required) 153 * @param value the field value 154 * @throws java.io.IOException on input/output errors 155 */ 156 public void writeField(String name, int value) 157 throws java.io.IOException { 158 writeField(name, Integer.toString(value)); 159 } 160 161 /** 162 * Writes an short field value. 163 * 164 * @param name the field name (required) 165 * @param value the field value 166 * @throws java.io.IOException on input/output errors 167 */ 168 public void writeField(String name, short value) 169 throws java.io.IOException { 170 writeField(name, Short.toString(value)); 171 } 172 173 /** 174 * Writes an char field value. 175 * 176 * @param name the field name (required) 177 * @param value the field value 178 * @throws java.io.IOException on input/output errors 179 */ 180 public void writeField(String name, char value) 181 throws java.io.IOException { 182 writeField(name, new Character(value).toString()); 183 } 184 185 /** 186 * Writes an string field value. If the value is null, an empty string 187 * is sent (""). 188 * 189 * @param name the field name (required) 190 * @param value the field value 191 * @throws java.io.IOException on input/output errors 192 */ 193 public void writeField(String name, String value) 194 throws java.io.IOException { 195 if(name == null) { 196 throw new IllegalArgumentException("Name cannot be null or empty."); 197 } 198 if(value == null) { 199 value = ""; 200 } 201 /* 202 --boundary\r\n 203 Content-Disposition: form-data; name="<fieldName>"\r\n 204 \r\n 205 <value>\r\n 206 */ 207 // write boundary 208 out.writeBytes(PREFIX); 209 out.writeBytes(boundary); 210 out.writeBytes(NEWLINE); 211 // write content header 212 out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\""); 213 out.writeBytes(NEWLINE); 214 out.writeBytes(NEWLINE); 215 // write content 216 out.writeBytes(value); 217 out.writeBytes(NEWLINE); 218 out.flush(); 219 } 220 221 /** 222 * Writes a file's contents. If the file is null, does not exists, or 223 * is a directory, a <code>java.lang.IllegalArgumentException</code> 224 * will be thrown. 225 * 226 * @param name the field name 227 * @param mimeType the file content type (optional, recommended) 228 * @param file the file (the file must exist) 229 * @throws java.io.IOException on input/output errors 230 */ 231 public void writeFile(String name, String mimeType, java.io.File file) 232 throws java.io.IOException { 233 if(file == null) { 234 throw new IllegalArgumentException("File cannot be null."); 235 } 236 if(!file.exists()) { 237 throw new IllegalArgumentException("File does not exist."); 238 } 239 if(file.isDirectory()) { 240 throw new IllegalArgumentException("File cannot be a directory."); 241 } 242 writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file)); 243 } 244 245 /** 246 * Writes a input stream's contents. If the input stream is null, a 247 * <code>java.lang.IllegalArgumentException</code> will be thrown. 248 * 249 * @param name the field name 250 * @param mimeType the file content type (optional, recommended) 251 * @param fileName the file name (required) 252 * @param is the input stream 253 * @throws java.io.IOException on input/output errors 254 */ 255 public void writeFile(String name, String mimeType, 256 String fileName, InputStream is) 257 throws java.io.IOException { 258 if(is == null) { 259 throw new IllegalArgumentException("Input stream cannot be null."); 260 } 261 if(fileName == null || fileName.length() == 0) { 262 throw new IllegalArgumentException("File name cannot be null or empty."); 263 } 264 /* 265 --boundary\r\n 266 Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n 267 Content-Type: <mime-type>\r\n 268 \r\n 269 <file-data>\r\n 270 */ 271 // write boundary 272 out.writeBytes(PREFIX); 273 out.writeBytes(boundary); 274 out.writeBytes(NEWLINE); 275 // write content header 276 out.writeBytes("Content-Disposition: form-data; name=\"" + name + 277 "\"; filename=\"" + fileName + "\""); 278 out.writeBytes(NEWLINE); 279 if(mimeType != null) { 280 out.writeBytes("Content-Type: " + mimeType); 281 out.writeBytes(NEWLINE); 282 } 283 out.writeBytes(NEWLINE); 284 // write content 285 byte[] data = new byte[1024]; 286 int r = 0; 287 while((r = is.read(data, 0, data.length)) != -1) { 288 out.write(data, 0, r); 289 } 290 // close input stream, but ignore any possible exception for it 291 try { 292 is.close(); 293 } catch(Exception e) {} 294 out.writeBytes(NEWLINE); 295 out.flush(); 296 } 297 298 /** 299 * Writes the given bytes. The bytes are assumed to be the contents 300 * of a file, and will be sent as such. If the data is null, a 301 * <code>java.lang.IllegalArgumentException</code> will be thrown. 302 * 303 * @param name the field name 304 * @param mimeType the file content type (optional, recommended) 305 * @param fileName the file name (required) 306 * @param data the file data 307 * @throws java.io.IOException on input/output errors 308 */ 309 public void writeFile(String name, String mimeType, 310 String fileName, byte[] data) 311 throws java.io.IOException { 312 if(data == null) { 313 throw new IllegalArgumentException("Data cannot be null."); 314 } 315 if(fileName == null || fileName.length() == 0) { 316 throw new IllegalArgumentException("File name cannot be null or empty."); 317 } 318 /* 319 --boundary\r\n 320 Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n 321 Content-Type: <mime-type>\r\n 322 \r\n 323 <file-data>\r\n 324 */ 325 // write boundary 326 out.writeBytes(PREFIX); 327 out.writeBytes(boundary); 328 out.writeBytes(NEWLINE); 329 // write content header 330 out.writeBytes("Content-Disposition: form-data; name=\"" + name + 331 "\"; filename=\"" + fileName + "\""); 332 out.writeBytes(NEWLINE); 333 if(mimeType != null) { 334 out.writeBytes("Content-Type: " + mimeType); 335 out.writeBytes(NEWLINE); 336 } 337 out.writeBytes(NEWLINE); 338 // write content 339 out.write(data, 0, data.length); 340 out.writeBytes(NEWLINE); 341 out.flush(); 342 } 343 344 /** 345 * Flushes the stream. Actually, this method does nothing, as the only 346 * write methods are highly specialized and automatically flush. 347 */ 348 public void flush() { 349 // out.flush(); 350 } 351 352 /** 353 * Closes the stream. <br/> 354 * <br/> 355 * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the 356 * multipart stream. 357 * 358 * @throws java.io.IOException on input/output errors 359 */ 360 public void close() throws java.io.IOException { 361 // write final boundary 362 out.writeBytes(PREFIX); 363 out.writeBytes(boundary); 364 out.writeBytes(PREFIX); 365 out.writeBytes(NEWLINE); 366 out.flush(); 367 out.close(); 368 } 369 370 /** 371 * Gets the multipart boundary string being used by this stream. 372 * 373 * @return the boundary 374 */ 375 public String getBoundary() { 376 return this.boundary; 377 } 378 379 /** 380 * Creates a new <code>java.net.URLConnection</code> object from the 381 * specified <code>java.net.URL</code>. This is a convenience method 382 * which will set the <code>doInput</code>, <code>doOutput</code>, 383 * <code>useCaches</code> and <code>defaultUseCaches</code> fields to 384 * the appropriate settings in the correct order. 385 * 386 * @return a <code>java.net.URLConnection</code> object for the URL 387 * @throws java.io.IOException on input/output errors 388 */ 389 public static URLConnection createConnection(URL url) 390 throws java.io.IOException { 391 URLConnection urlConn = url.openConnection(); 392 if(urlConn instanceof HttpURLConnection) { 393 HttpURLConnection httpConn = (HttpURLConnection)urlConn; 394 httpConn.setRequestMethod("POST"); 395 } 396 urlConn.setDoInput(true); 397 urlConn.setDoOutput(true); 398 urlConn.setUseCaches(false); 399 urlConn.setDefaultUseCaches(false); 400 return urlConn; 401 } 402 403 /** 404 * Creates a multipart boundary string by concatenating 20 hyphens (-) 405 * and the hexadecimal (base-16) representation of the current time in 406 * milliseconds. 407 * 408 * @return a multipart boundary string 409 * @see #getContentType(String) 410 */ 411 public static String createBoundary() { 412 return "--------------------" + 413 Long.toString(System.currentTimeMillis(), 16); 414 } 415 416 /** 417 * Gets the content type string suitable for the 418 * <code>java.net.URLConnection</code> which includes the multipart 419 * boundary string. <br/> 420 * <br/> 421 * This method is static because, due to the nature of the 422 * <code>java.net.URLConnection</code> class, once the output stream 423 * for the connection is acquired, it's too late to set the content 424 * type (or any other request parameter). So one has to create a 425 * multipart boundary string first before using this class, such as 426 * with the <code>createBoundary()</code> method. 427 * 428 * @param boundary the boundary string 429 * @return the content type string 430 * @see #createBoundary() 431 */ 432 public static String getContentType(String boundary) { 433 return "multipart/form-data; boundary=" + boundary; 434 } 435 435 } -
trunk/src/org/openstreetmap/josm/io/MyHttpHandler.java
r655 r1169 26 26 proxyPort = port; 27 27 } 28 28 29 29 protected java.net.URLConnection openConnection(URL u, Proxy p) 30 30 throws IOException { -
trunk/src/org/openstreetmap/josm/io/NmeaReader.java
r1167 r1169 28 28 public class NmeaReader { 29 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 30 /** Handler for the different types that NMEA speaks. */ 31 public static enum NMEA_TYPE { 32 33 /** RMC = recommended minimum sentence C. */ 34 GPRMC("$GPRMC"), 35 /** GPS positions. */ 36 GPGGA("$GPGGA"), 37 /** SA = satellites active. */ 38 GPGSA("$GPGSA"), 39 /** Course over ground and ground speed */ 40 GPVTG("$GPVTG"); 41 42 private final String type; 43 44 NMEA_TYPE(String type) { 45 this.type = type; 46 } 47 48 public String getType() { 49 return this.type; 50 } 51 52 public boolean equals(String type) { 53 return this.type.equals(type); 54 } 55 } 56 57 // GPVTG 58 public static enum GPVTG { 59 COURSE(1),COURSE_REF(2), // true course 60 COURSE_M(3), COURSE_M_REF(4), // magnetic course 61 SPEED_KN(5), SPEED_KN_UNIT(6), // speed in knots 62 SPEED_KMH(7), SPEED_KMH_UNIT(8), // speed in km/h 63 REST(9); // version-specific rest 64 65 public final int position; 66 67 GPVTG(int position) { 68 this.position = position; 69 } 70 } 71 72 // The following only applies to GPRMC 73 public static enum GPRMC { 74 TIME(1), 75 /** Warning from the receiver (A = data ok, V = warning) */ 76 RECEIVER_WARNING(2), 77 WIDTH_NORTH(3), WIDTH_NORTH_NAME(4), // Latitude, NS 78 LENGTH_EAST(5), LENGTH_EAST_NAME(6), // Longitude, EW 79 SPEED(7), COURSE(8), DATE(9), // Speed in knots 80 MAGNETIC_DECLINATION(10), UNKNOWN(11), // magnetic declination 81 /** 82 * Mode (A = autonom; D = differential; E = estimated; N = not valid; S 83 * = simulated) 84 * 85 * @since NMEA 2.3 86 */ 87 MODE(12); 88 89 public final int position; 90 91 GPRMC(int position) { 92 this.position = position; 93 } 94 } 95 96 // The following only applies to GPGGA 97 public static enum GPGGA { 98 TIME(1), LATITUDE(2), LATITUDE_NAME(3), LONGITUDE(4), LONGITUDE_NAME(5), 99 /** 100 * Quality (0 = invalid, 1 = GPS, 2 = DGPS, 6 = estimanted (@since NMEA 101 * 2.3)) 102 */ 103 QUALITY(6), SATELLITE_COUNT(7), 104 HDOP(8), // HDOP (horizontal dilution of precision) 105 HEIGHT(9), HEIGHT_UNTIS(10), // height above NN (above geoid) 106 HEIGHT_2(11), HEIGHT_2_UNTIS(12), // height geoid - height ellipsoid (WGS84) 107 GPS_AGE(13),// Age of differential GPS data 108 REF(14); // REF station 109 110 public final int position; 111 GPGGA(int position) { 112 this.position = position; 113 } 114 } 115 116 public static enum GPGSA { 117 AUTOMATIC(1), 118 FIX_TYPE(2), // 1 = not fixed, 2 = 2D fixed, 3 = 3D fixed) 119 // PRN numbers for max 12 satellites 120 PRN_1(3), PRN_2(4), PRN_3(5), PRN_4(6), PRN_5(7), PRN_6(8), 121 PRN_7(9), PRN_8(10), PRN_9(11), PRN_10(12), PRN_11(13), PRN_12(14), 122 PDOP(15), // PDOP (precision) 123 HDOP(16), // HDOP (horizontal precision) 124 VDOP(17), ; // VDOP (vertical precision) 125 126 public final int position; 127 GPGSA(int position) { 128 this.position = position; 129 } 130 } 131 132 public GpxData data; 133 133 134 134 // private final static SimpleDateFormat GGATIMEFMT = 135 135 // new SimpleDateFormat("HHmmss.SSS"); 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 136 private final static SimpleDateFormat RMCTIMEFMT = 137 new SimpleDateFormat("ddMMyyHHmmss.SSS"); 138 139 // functons for reading the error stats 140 public NMEAParserState ps; 141 142 public int getParserUnknown() { 143 return ps.unknown; 144 } 145 public int getParserZeroCoordinates() { 146 return ps.zero_coord; 147 } 148 public int getParserChecksumErrors() { 149 return ps.checksum_errors; 150 } 151 public int getParserMalformed() { 152 return ps.malformed; 153 } 154 public int getNumberOfCoordinates() { 155 return ps.success; 156 } 157 158 public NmeaReader(InputStream source, File relativeMarkerPath) { 159 160 // create the data tree 161 data = new GpxData(); 162 GpxTrack currentTrack = new GpxTrack(); 163 data.tracks.add(currentTrack); 164 165 try { 166 BufferedReader rd = 167 new BufferedReader(new InputStreamReader(source)); 168 169 StringBuffer sb = new StringBuffer(1024); 170 int loopstart_char = rd.read(); 171 if(loopstart_char == -1) {// zero size file 172 //TODO tell user about the problem? 173 return; 174 } 175 sb.append((char)loopstart_char); 176 ps = new NMEAParserState(); 177 ps.p_Date="010100"; // TODO date problem 178 while(true) { 179 // don't load unparsable files completely to memory 180 if(sb.length()>=1020) sb.delete(0, sb.length()-1); 181 int c = rd.read(); 182 if(c=='$') { 183 ParseNMEASentence(sb.toString(), ps); 184 sb.delete(0, sb.length()); 185 sb.append('$'); 186 } else if(c == -1) { 187 // EOF: add last WayPoint if it works out 188 ParseNMEASentence(sb.toString(),ps); 189 break; 190 } else sb.append((char)c); 191 } 192 rd.close(); 193 Object[] wparr = ps.waypoints.toArray(); 194 currentTrack.trackSegs.add(ps.waypoints); 195 data.recalculateBounds(); 196 197 } catch (final IOException e) { 198 // TODO tell user about the problem? 199 } 200 } 201 private class NMEAParserState { 202 protected Collection<WayPoint> waypoints = new ArrayList<WayPoint>(); 203 protected String p_Time; 204 protected String p_Date; 205 protected WayPoint p_Wp; 206 207 protected int success = 0; // number of successfully parsend sentences 208 protected int malformed = 0; 209 protected int checksum_errors = 0; 210 protected int unknown = 0; 211 protected int zero_coord = 0; 212 } 213 214 // Parses split up sentences into WayPoints which are stored 215 // in the collection in the NMEAParserState object. 216 // Returns true if the input made sence, false otherwise. 217 private boolean ParseNMEASentence(String s, NMEAParserState ps) { 218 try { 219 if(s.equals("")) throw(null); 220 221 // checksum check: 222 // the bytes between the $ and the * are xored; 223 // if there is no * or other meanities it will throw 224 // and result in a malformed packet. 225 String[] chkstrings = s.split("\\*"); 226 byte[] chb = chkstrings[0].getBytes(); 227 int chk=0; 228 for(int i = 1; i < chb.length; i++) chk ^= chb[i]; 229 if(Integer.parseInt(chkstrings[1].substring(0,2),16) != chk) { 230 //System.out.println("Checksum error"); 231 ps.checksum_errors++; 232 ps.p_Wp=null; 233 return false; 234 } 235 // now for the content 236 String[] e = chkstrings[0].split(","); 237 String accu; 238 239 WayPoint currentwp = ps.p_Wp; 240 String currentDate = ps.p_Date; 241 242 // handle the packet content 243 if(e[0].equals("$GPGGA")) { 244 // Position 245 LatLon latLon = parseLatLon( 246 e[GPGGA.LATITUDE_NAME.position], 247 e[GPGGA.LONGITUDE_NAME.position], 248 e[GPGGA.LATITUDE.position], 249 e[GPGGA.LONGITUDE.position] 250 ); 251 if(latLon==null) throw(null); // malformed 252 253 if((latLon.lat()==0.0) && (latLon.lon()==0.0)) { 254 ps.zero_coord++; 255 return false; 256 } 257 258 // time 259 accu = e[GPGGA.TIME.position]; 260 Date d = RMCTIMEFMT.parse(currentDate+accu, new ParsePosition(0)); 261 if (d == null) throw(null); // malformed 262 263 if((ps.p_Time==null) || (currentwp==null) || !ps.p_Time.equals(accu)) { 264 // this node is newer than the previous, create a new waypoint. 265 // no matter if previous WayPoint was null, we got something 266 // better now. 267 ps.p_Time=accu; 268 currentwp = new WayPoint(latLon); 269 } 270 if(!currentwp.attr.containsKey("time")) { 271 // As this sentence has no complete time only use it 272 // if there is no time so far 273 String gpxdate = WayPoint.GPXTIMEFMT.format(d); 274 currentwp.attr.put("time", gpxdate); 275 } 276 // elevation 277 accu=e[GPGGA.HEIGHT_UNTIS.position]; 278 if(accu.equals("M")) { 279 // Ignore heights that are not in meters for now 280 accu=e[GPGGA.HEIGHT.position]; 281 if(!accu.equals("")) { 282 Double.parseDouble(accu); 283 // if it throws it's malformed; this should only happen if the 284 // device sends nonstandard data. 285 if(!accu.equals("")) currentwp.attr.put("ele", accu); 286 } 287 } 288 // number of sattelites 289 accu=e[GPGGA.SATELLITE_COUNT.position]; 290 int sat = 0; 291 if(!accu.equals("")) { 292 sat = Integer.parseInt(accu); 293 currentwp.attr.put("sat", accu); 294 } 295 // h-dilution 296 accu=e[GPGGA.HDOP.position]; 297 if(!accu.equals("")) { 298 Double.parseDouble(accu); 299 currentwp.attr.put("hdop", accu); 300 } 301 // fix 302 accu=e[GPGGA.QUALITY.position]; 303 if(!accu.equals("")) { 304 int fixtype = Integer.parseInt(accu); 305 switch(fixtype) { 306 case 0: 307 currentwp.attr.put("fix", "none"); 308 break; 309 case 1: 310 if(sat < 4) currentwp.attr.put("fix", "2d"); 311 else currentwp.attr.put("fix", "3d"); 312 break; 313 case 2: 314 currentwp.attr.put("fix", "dgps"); 315 break; 316 default: 317 break; 318 } 319 } 320 } else if(e[0].equals("$GPVTG")) { 321 // COURSE 322 accu = e[GPVTG.COURSE_REF.position]; 323 if(accu.equals("T")) { 324 // other values than (T)rue are ignored 325 accu = e[GPVTG.COURSE.position]; 326 if(!accu.equals("")) { 327 Double.parseDouble(accu); 328 currentwp.attr.put("course", accu); 329 } 330 } 331 // SPEED 332 accu = e[GPVTG.SPEED_KMH_UNIT.position]; 333 if(accu.startsWith("K")) { 334 accu = e[GPVTG.SPEED_KMH.position]; 335 if(!accu.equals("")) { 336 double speed = Double.parseDouble(accu); 337 speed /= 3.6; // speed in m/s 338 currentwp.attr.put("speed", Double.toString(speed)); 339 } 340 } 341 } else if(e[0].equals("$GPGSA")) { 342 // vdop 343 accu=e[GPGSA.VDOP.position]; 344 if(!accu.equals("")) { 345 Double.parseDouble(accu); 346 currentwp.attr.put("vdop", accu); 347 } 348 // hdop 349 accu=e[GPGSA.HDOP.position]; 350 if(!accu.equals("")) { 351 Double.parseDouble(accu); 352 currentwp.attr.put("hdop", accu); 353 } 354 // pdop 355 accu=e[GPGSA.PDOP.position]; 356 if(!accu.equals("")) { 357 Double.parseDouble(accu); 358 currentwp.attr.put("pdop", accu); 359 } 360 } 361 else if(e[0].equals("$GPRMC")) { 362 // coordinates 363 LatLon latLon = parseLatLon( 364 e[GPRMC.WIDTH_NORTH_NAME.position], 365 e[GPRMC.LENGTH_EAST_NAME.position], 366 e[GPRMC.WIDTH_NORTH.position], 367 e[GPRMC.LENGTH_EAST.position] 368 ); 369 if(latLon==null) throw(null); 370 if((latLon.lat()==0.0) && (latLon.lon()==0.0)) { 371 ps.zero_coord++; 372 return false; 373 } 374 // time 375 currentDate = e[GPRMC.DATE.position]; 376 String time = e[GPRMC.TIME.position]; 377 378 Date d = RMCTIMEFMT.parse(currentDate+time, new ParsePosition(0)); 379 if (d == null) throw(null); 380 381 if((ps.p_Time==null) || (currentwp==null) || !ps.p_Time.equals(time)) { 382 // this node is newer than the previous, create a new waypoint. 383 ps.p_Time=time; 384 currentwp = new WayPoint(latLon); 385 } 386 // time: this sentence has complete time so always use it. 387 String gpxdate = WayPoint.GPXTIMEFMT.format(d); 388 currentwp.attr.put("time", gpxdate); 389 // speed 390 accu = e[GPRMC.SPEED.position]; 391 if(!accu.equals("") && !currentwp.attr.containsKey("speed")) { 392 double speed = Double.parseDouble(accu); 393 speed *= 0.514444444; // to m/s 394 currentwp.attr.put("speed", Double.toString(speed)); 395 } 396 // course 397 accu = e[GPRMC.COURSE.position]; 398 if(!accu.equals("") && !currentwp.attr.containsKey("course")) { 399 Double.parseDouble(accu); 400 currentwp.attr.put("course", accu); 401 } 402 403 // TODO fix? 404 // * Mode (A = autonom; D = differential; E = estimated; N = not valid; S 405 // * = simulated) 406 // * 407 // * @since NMEA 2.3 408 // 409 //MODE(12); 410 } else { 411 ps.unknown++; 412 return false; 413 } 414 ps.p_Date = currentDate; 415 if(ps.p_Wp != currentwp) { 416 if(ps.p_Wp!=null) { 417 ps.p_Wp.setTime(); 418 } 419 ps.p_Wp = currentwp; 420 ps.waypoints.add(currentwp); 421 ps.success++; 422 return true; 423 } 424 return true; 425 426 } catch(Exception x) { 427 // out of bounds and such 428 //System.out.println("Malformed line: "+s.toString().trim()); 429 ps.malformed++; 430 ps.p_Wp=null; 431 return false; 432 } 433 } 434 435 private LatLon parseLatLon(String ew, String ns, String dlat, String dlon) 436 throws NumberFormatException { 437 String widthNorth = dlat.trim(); 438 String lengthEast = dlon.trim(); 439 440 // return a zero latlon instead of null so it is logged as zero coordinate 441 // instead of malformed sentence 442 if(widthNorth.equals("")&&lengthEast.equals("")) return new LatLon(0.0,0.0); 443 444 // The format is xxDDLL.LLLL 445 // xx optional whitespace 446 // DD (int) degres 447 // LL.LLLL (double) latidude 448 int latdegsep = widthNorth.indexOf('.') - 2; 449 if (latdegsep < 0) return null; 450 451 int latdeg = Integer.parseInt(widthNorth.substring(0, latdegsep)); 452 double latmin = Double.parseDouble(widthNorth.substring(latdegsep)); 453 double lat = latdeg + latmin / 60; 454 if ("S".equals(ns)) { 455 if(!ew.equals("N")) return null; 456 lat = -lat; 457 } 458 459 int londegsep = lengthEast.indexOf('.') - 2; 460 if (londegsep < 0) return null; 461 462 int londeg = Integer.parseInt(lengthEast.substring(0, londegsep)); 463 double lonmin = Double.parseDouble(lengthEast.substring(londegsep)); 464 double lon = londeg + lonmin / 60; 465 if ("W".equals(ew)) { 466 if(!ew.equals("E")) return null; 467 lon = -lon; 468 } 469 return new LatLon(lat, lon); 470 } 471 471 } -
trunk/src/org/openstreetmap/josm/io/OsmConnection.java
r763 r1169 34 34 public class OsmConnection { 35 35 36 37 38 39 40 41 36 public static class OsmParseException extends Exception { 37 public OsmParseException() {super();} 38 public OsmParseException(String message, Throwable cause) {super(message, cause);} 39 public OsmParseException(String message) {super(message);} 40 public OsmParseException(Throwable cause) {super(cause);} 41 } 42 42 43 44 43 protected boolean cancel = false; 44 protected HttpURLConnection activeConnection; 45 45 46 47 48 49 50 51 52 53 54 46 private static OsmAuth authentication = new OsmAuth(); 47 /** 48 * Initialize the http defaults and the authenticator. 49 */ 50 static { 51 //TODO: refactor this crap (maybe just insert the damn auth http-header by yourself) 52 try { 53 HttpURLConnection.setFollowRedirects(true); 54 Authenticator.setDefault(authentication); 55 55 } catch (SecurityException e) { 56 56 } 57 57 } 58 58 59 60 61 62 63 64 65 66 67 68 69 70 59 /** 60 * The authentication class handling the login requests. 61 */ 62 private static class OsmAuth extends Authenticator { 63 /** 64 * Set to true, when the autenticator tried the password once. 65 */ 66 boolean passwordtried = false; 67 /** 68 * Whether the user cancelled the password dialog 69 */ 70 boolean authCancelled = false; 71 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 72 @Override protected PasswordAuthentication getPasswordAuthentication() { 73 String username = Main.pref.get("osm-server.username"); 74 String password = Main.pref.get("osm-server.password"); 75 if (passwordtried || username.equals("") || password.equals("")) { 76 JPanel p = new JPanel(new GridBagLayout()); 77 if (!username.equals("") && !password.equals("")) 78 p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop()); 79 p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,10,0)); 80 JTextField usernameField = new JTextField(username, 20); 81 p.add(usernameField, GBC.eol()); 82 p.add(new JLabel(tr("Password")), GBC.std().insets(0,0,10,0)); 83 JPasswordField passwordField = new JPasswordField(password, 20); 84 p.add(passwordField, GBC.eol()); 85 JLabel warning = new JLabel(tr("Warning: The password is transferred unencrypted.")); 86 warning.setFont(warning.getFont().deriveFont(Font.ITALIC)); 87 p.add(warning, GBC.eop()); 88 88 89 90 89 JCheckBox savePassword = new JCheckBox(tr("Save user and password (unencrypted)"), !username.equals("") && !password.equals("")); 90 p.add(savePassword, GBC.eop()); 91 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 92 int choice = JOptionPane.showConfirmDialog(Main.parent, p, tr("Enter Password"), JOptionPane.OK_CANCEL_OPTION); 93 if (choice == JOptionPane.CANCEL_OPTION) { 94 authCancelled = true; 95 return null; 96 } 97 username = usernameField.getText(); 98 password = String.valueOf(passwordField.getPassword()); 99 if (savePassword.isSelected()) { 100 Main.pref.put("osm-server.username", username); 101 Main.pref.put("osm-server.password", password); 102 } 103 if (username.equals("")) 104 return null; 105 } 106 passwordtried = true; 107 return new PasswordAuthentication(username, password.toCharArray()); 108 } 109 } 110 110 111 112 113 114 115 116 117 111 /** 112 * Must be called before each connection attemp to initialize the authentication. 113 */ 114 protected final void initAuthentication() { 115 authentication.authCancelled = false; 116 authentication.passwordtried = false; 117 } 118 118 119 120 121 122 123 124 119 /** 120 * @return Whether the connection was cancelled. 121 */ 122 protected final boolean isAuthCancelled() { 123 return authentication.authCancelled; 124 } 125 125 126 127 128 129 130 131 132 133 134 135 136 137 126 public void cancel() { 127 Main.pleaseWaitDlg.currentAction.setText(tr("Aborting...")); 128 cancel = true; 129 if (activeConnection != null) { 130 activeConnection.setConnectTimeout(100); 131 activeConnection.setReadTimeout(100); 132 try { 133 Thread.sleep(100); 134 } catch (InterruptedException ex) {} 135 activeConnection.disconnect(); 136 } 137 } 138 138 139 140 141 142 143 144 139 protected void addAuth(HttpURLConnection con) throws CharacterCodingException { 140 CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); 141 String auth = Main.pref.get("osm-server.username") + ":" + Main.pref.get("osm-server.password"); 142 ByteBuffer bytes = encoder.encode(CharBuffer.wrap(auth)); 143 con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes)); 144 } 145 145 } -
trunk/src/org/openstreetmap/josm/io/OsmIdReader.java
r627 r1169 21 21 /** 22 22 * Read only the ids and classes of an stream. 23 * 23 * 24 24 * @author Imi 25 25 */ 26 26 public class OsmIdReader extends DefaultHandler { 27 27 28 29 30 28 private boolean cancel; 29 Map<Long, String> entries = new HashMap<Long, String>(); 30 private Reader in; 31 31 32 33 34 35 36 37 38 39 40 32 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 33 if (qName.equals("node") || qName.equals("way") || qName.equals("relation")) { 34 try { 35 entries.put(Long.valueOf(atts.getValue("id")), qName); 36 } catch (Exception e) { 37 e.printStackTrace(); 38 throw new SAXException(tr("Error during parse.")); 39 } 40 } 41 41 } 42 42 43 43 public Map<Long, String> parseIds(InputStream in) throws IOException, SAXException { 44 44 this.in = new InputStreamReader(in, "UTF-8"); 45 46 45 try { 46 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(this.in), this); 47 47 } catch (ParserConfigurationException e) { 48 49 50 51 48 if (!cancel) { 49 e.printStackTrace(); // broken SAXException chaining 50 throw new SAXException(e); 51 } 52 52 } catch (SAXException e) { 53 54 55 } 56 57 58 59 60 61 53 if (!cancel) 54 throw e; 55 } 56 return entries; 57 } 58 59 public void cancel() { 60 cancel = true; 61 if (in != null) 62 62 try {in.close();} catch (IOException e) {} 63 63 } 64 64 } -
trunk/src/org/openstreetmap/josm/io/OsmReader.java
r1071 r1169 106 106 private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>(); 107 107 108 /** 108 /** 109 109 * Data structure for relation objects 110 110 */ 111 111 private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>(); 112 112 113 /** 113 /** 114 114 * List of protocol versions that will be accepted on reading 115 115 */ … … 133 133 generator = atts.getValue("generator"); 134 134 135 135 136 136 } else if (qName.equals("bound")) { 137 137 // old style bounds. 138 138 // TODO: remove this around 1st October 2008. 139 // - this is a bit of a hack; since we still write out old style bound objects, 139 // - this is a bit of a hack; since we still write out old style bound objects, 140 140 // we don't want to load them both. so when writing, we add a "note" tag the our 141 141 // old-style bound, and when reading, ignore those with a "note". … … 172 172 ds.dataSources.add(src); 173 173 } 174 174 175 175 // ---- PARSING NODES AND WAYS ---- 176 176 177 177 } else if (qName.equals("node")) { 178 178 current = new Node(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon"))); … … 192 192 list.add(id); 193 193 194 // ---- PARSING RELATIONS ---- 194 // ---- PARSING RELATIONS ---- 195 195 196 196 } else if (qName.equals("relation")) { … … 207 207 emd.type=atts.getValue("type"); 208 208 emd.relationMember.role = atts.getValue("role"); 209 209 210 210 if (emd.id == 0) 211 211 throw new SAXException(tr("Incomplete <member> specification with ref=0")); 212 212 213 213 list.add(emd); 214 214 215 215 // ---- PARSING TAGS (applicable to all objects) ---- 216 216 217 217 } else if (qName.equals("tag")) { 218 218 current.put(atts.getValue("k"), atts.getValue("v")); … … 231 231 } 232 232 } 233 234 /** 233 234 /** 235 235 * Constructor initializes list of allowed protocol versions. 236 236 */ … … 239 239 allowedVersions.add(Main.pref.get("osm-server.version", "0.5")); 240 240 // now also add all compatible versions 241 String[] additionalVersions = 241 String[] additionalVersions = 242 242 Main.pref.get("osm-server.additional-versions", "").split("/,/"); 243 243 if (additionalVersions.length == 1 && additionalVersions[0].length() == 0) 244 244 additionalVersions = new String[] {}; 245 allowedVersions.addAll(Arrays.asList(additionalVersions)); 245 allowedVersions.addAll(Arrays.asList(additionalVersions)); 246 246 } 247 247 … … 267 267 current.timestamp = time; 268 268 } 269 269 270 270 // user attribute added in 0.4 API 271 271 String user = atts.getValue("user"); … … 274 274 current.user = User.get(user); 275 275 } 276 276 277 277 // visible attribute added in 0.4 API 278 278 String visible = atts.getValue("visible"); … … 342 342 adder.visit(w); 343 343 } 344 344 345 345 } 346 346 … … 348 348 * Return the Way object with the given id, or null if it doesn't 349 349 * exist yet. This method only looks at ways stored in the data set. 350 * 350 * 351 351 * @param id 352 352 * @return way object or null … … 365 365 * Return the Relation object with the given id, or null if it doesn't 366 366 * exist yet. This method only looks at relations stored in the data set. 367 * 367 * 368 368 * @param id 369 369 * @return relation object or null … … 380 380 381 381 /** 382 * Create relations. This is slightly different than n/s/w because 382 * Create relations. This is slightly different than n/s/w because 383 383 * unlike other objects, relations may reference other relations; it 384 384 * is not guaranteed that a referenced relation will have been created … … 387 387 */ 388 388 private void createRelations() { 389 389 390 390 // pass 1 - create all relations 391 391 for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) { … … 399 399 Relation en = findRelation(e.getKey().id); 400 400 if (en == null) throw new Error("Failed to create relation " + e.getKey().id); 401 401 402 402 for (RelationMemberData emd : e.getValue()) { 403 403 RelationMember em = emd.relationMember; -
trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java
r1146 r1169 12 12 13 13 public class OsmServerLocationReader extends OsmServerReader { 14 14 15 15 String url; 16 16 17 17 public OsmServerLocationReader(String url) { 18 18 this.url = url; 19 19 } 20 20 21 21 /** 22 22 * Method to download OSM files from somewhere -
trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java
r1146 r1169 20 20 String type; 21 21 boolean full; 22 22 23 23 public OsmServerObjectReader(long id, String type, boolean full) { 24 24 this.id = id; -
trunk/src/org/openstreetmap/josm/io/OsmServerReader.java
r1146 r1169 19 19 /** 20 20 * This DataReader reads directly from the REST API of the osm server. 21 * 21 * 22 22 * It supports plain text transfer as well as gzip or deflate encoded transfers; 23 23 * if compressed transfers are unwanted, set property osm-server.use-compression … … 27 27 */ 28 28 public abstract class OsmServerReader extends OsmConnection { 29 30 31 32 33 34 35 36 37 38 29 /** 30 * Open a connection to the given url and return a reader on the input stream 31 * from that connection. In case of user cancel, return <code>null</code>. 32 * @param urlStr The exact url to connect to. 33 * @param pleaseWaitDlg 34 * @return An reader reading the input stream (servers answer) or <code>null</code>. 35 */ 36 protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException { 37 String version = Main.pref.get("osm-server.version", "0.5"); 38 urlStr = Main.pref.get("osm-server.url")+"/"+version+"/" + urlStr; 39 39 return getInputStreamRaw(urlStr, pleaseWaitDlg); 40 40 } 41 41 42 42 protected InputStream getInputStreamRaw(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException { 43 44 System.out.println("download: "+urlStr);45 initAuthentication();46 URL url = new URL(urlStr);47 activeConnection = (HttpURLConnection)url.openConnection();48 if (cancel) {49 activeConnection.disconnect();50 return null;51 }52 53 if (Boolean.parseBoolean(Main.pref.get("osm-server.use-compression", "true")))54 activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");55 43 56 activeConnection.setConnectTimeout(15000);57 if (isAuthCancelled() && activeConnection.getResponseCode() == 401) 58 return null;59 if (activeConnection.getResponseCode() == 500) 60 61 throw new IOException(tr("Server returned internal error. Try a reduced area or retry after waiting some time."));62 } 63 // System.out.println("got return: "+activeConnection.getResponseCode()); 44 System.out.println("download: "+urlStr); 45 initAuthentication(); 46 URL url = new URL(urlStr); 47 activeConnection = (HttpURLConnection)url.openConnection(); 48 if (cancel) { 49 activeConnection.disconnect(); 50 return null; 51 } 64 52 65 String encoding = activeConnection.getContentEncoding(); 66 InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg); 67 if (encoding != null && encoding.equalsIgnoreCase("gzip")) { 68 inputStream = new GZIPInputStream(inputStream); 69 } 70 else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { 71 inputStream = new InflaterInputStream(inputStream, new Inflater(true)); 72 } 73 return inputStream; 74 } 75 53 if (Boolean.parseBoolean(Main.pref.get("osm-server.use-compression", "true"))) 54 activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); 55 56 activeConnection.setConnectTimeout(15000); 57 if (isAuthCancelled() && activeConnection.getResponseCode() == 401) 58 return null; 59 if (activeConnection.getResponseCode() == 500) 60 { 61 throw new IOException(tr("Server returned internal error. Try a reduced area or retry after waiting some time.")); 62 } 63 // System.out.println("got return: "+activeConnection.getResponseCode()); 64 65 String encoding = activeConnection.getContentEncoding(); 66 InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg); 67 if (encoding != null && encoding.equalsIgnoreCase("gzip")) { 68 inputStream = new GZIPInputStream(inputStream); 69 } 70 else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { 71 inputStream = new InflaterInputStream(inputStream, new Inflater(true)); 72 } 73 return inputStream; 74 } 75 76 76 public abstract DataSet parseOsm() throws SAXException, IOException; 77 77 78 78 } -
trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
r1137 r1169 79 79 80 80 long uploadStartTime; 81 81 82 82 public String timeLeft(int progress, int list_size) { 83 83 long now = System.currentTimeMillis(); … … 96 96 return time_left_str; 97 97 } 98 98 99 99 public void uploadOsm(Collection<OsmPrimitive> list) throws SAXException { 100 100 processed = new LinkedList<OsmPrimitive>(); … … 103 103 Main.pleaseWaitDlg.progress.setMaximum(list.size()); 104 104 Main.pleaseWaitDlg.progress.setValue(0); 105 105 106 106 // controls whether or not we open and close a changeset. API 0.6 requires changesets. 107 107 boolean useChangesets = Main.pref.get("osm-server.version", "0.5").equals("0.6"); 108 108 109 109 // controls whether or not we try and uplaod the whole bunch in one go 110 boolean useDiffUploads = Main.pref.getBoolean("osm-server.atomic-upload", 110 boolean useDiffUploads = Main.pref.getBoolean("osm-server.atomic-upload", 111 111 Main.pref.get("osm-server.version", "0.5").equals("0.6")); 112 112 113 113 String comment = null; 114 114 while (useChangesets && comment == null) { 115 comment = JOptionPane.showInputDialog(Main.parent, 115 comment = JOptionPane.showInputDialog(Main.parent, 116 116 tr("Provide a brief comment as to the changes to you are uploading:"), 117 117 tr("Commit comment"), JOptionPane.QUESTION_MESSAGE); … … 131 131 return; 132 132 } 133 133 134 134 try { 135 135 if (useDiffUploads) { … … 166 166 } 167 167 catch (OsmTransferException ex) { 168 dealWithTransferException(ex); 168 dealWithTransferException(ex); 169 169 } 170 170 dealWithTransferException(e); 171 171 } 172 172 } 173 173 174 174 /* FIXME: This code is terrible, please fix it!!!! */ 175 175 … … 183 183 * can fix the issue where hitting cancel doesn't do anything while 184 184 * retrying. - Mv0 Apr 2008 185 * 185 * 186 186 * Cancelling has an effect now, maybe it does not always catch on. Florian Heer, Aug 08 187 187 */ … … 198 198 Main.pref.get("osm-server.url") + 199 199 "/" + version + 200 "/" + "changeset" + 200 "/" + "changeset" + 201 201 "/" + "create"); 202 202 System.out.print("upload to: "+url+ "..." ); … … 205 205 activeConnection.setRequestMethod("PUT"); 206 206 addAuth(activeConnection); 207 207 208 208 activeConnection.setDoOutput(true); 209 209 OutputStream out = activeConnection.getOutputStream(); 210 210 OsmWriter.output(out, changeset); 211 211 out.close(); 212 212 213 213 activeConnection.connect(); 214 214 System.out.println("connected"); … … 224 224 } 225 225 if (retCode != 200 && retCode != 412) { 226 226 227 227 if (retries >= 0) { 228 228 retries--; … … 232 232 return startChangeset(retries, comment); 233 233 } 234 234 235 235 // Look for a detailed error message from the server 236 236 retMsg += "\n" + readString(activeConnection.getInputStream()); … … 262 262 throw new OsmTransferException (e.getMessage()+ " " + e.getClass().getCanonicalName(), e); 263 263 } 264 264 265 265 catch (Exception e) { 266 266 if (cancel) … … 274 274 return true; 275 275 } 276 276 277 277 private void uploadDiff(int retries, Collection<OsmPrimitive> list) throws OsmTransferException { 278 278 279 279 CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset); 280 280 … … 290 290 String diff = duv.getDocument(); 291 291 System.out.println(diff); 292 292 293 293 Main.pleaseWaitDlg.currentAction.setText(tr("Uploading...")); 294 294 try { … … 299 299 Main.pref.get("osm-server.url") + 300 300 "/" + version + 301 "/" + "changeset" + 301 "/" + "changeset" + 302 302 "/" + changeset.id + 303 303 "/upload" ); … … 307 307 activeConnection.setRequestMethod("POST"); 308 308 addAuth(activeConnection); 309 309 310 310 activeConnection.setDoOutput(true); 311 311 PrintWriter out; … … 317 317 out.print(diff); 318 318 out.close(); 319 319 320 320 activeConnection.connect(); 321 321 System.out.println("connected"); … … 323 323 int retCode = activeConnection.getResponseCode(); 324 324 String retMsg = ""; 325 325 326 326 if (retCode == 200) { 327 327 DiffResultReader.parseDiffResult(activeConnection.getInputStream(), list, processed, duv.getNewIdMap(), Main.pleaseWaitDlg); … … 333 333 System.out.println("retrying ("+retries+" left)"); 334 334 stopChangeset(retries); 335 } else { 335 } else { 336 336 // Look for a detailed error message from the server 337 337 retMsg += "\n" + readString(activeConnection.getInputStream()); … … 372 372 } 373 373 } 374 375 374 375 376 376 private void stopChangeset(int retries) throws OsmTransferException { 377 377 Main.pleaseWaitDlg.currentAction.setText(tr("Closing changeset...")); … … 383 383 Main.pref.get("osm-server.url") + 384 384 "/" + version + 385 "/" + "changeset" + 385 "/" + "changeset" + 386 386 "/" + changeset.id + 387 387 "/close" ); … … 391 391 activeConnection.setRequestMethod("PUT"); 392 392 addAuth(activeConnection); 393 393 394 394 activeConnection.setDoOutput(true); 395 395 OutputStream out = activeConnection.getOutputStream(); 396 396 OsmWriter.output(out, changeset); 397 397 out.close(); 398 398 399 399 activeConnection.connect(); 400 400 System.out.println("connected"); … … 415 415 System.out.println("retrying ("+retries+" left)"); 416 416 stopChangeset(retries); 417 } else { 417 } else { 418 418 // Look for a detailed error message from the server 419 419 retMsg += readString(activeConnection.getInputStream()); 420 420 421 421 // Report our error 422 422 ByteArrayOutputStream o = new ByteArrayOutputStream(); … … 490 490 processed.add(e); 491 491 } 492 492 493 493 /** 494 494 * Read a long from the input stream and return it. … … 540 540 new URL(Main.pref.get("osm-server.url") + 541 541 "/" + version + "/"), 542 urlSuffix + 542 urlSuffix + 543 543 "/" + (osm.id==0 ? "create" : osm.id), 544 544 new MyHttpHandler()); … … 622 622 } 623 623 } 624 624 625 625 private void sendRequest(String requestMethod, String urlSuffix, 626 626 OsmPrimitive osm, boolean addBody) { … … 636 636 } 637 637 } 638 638 639 639 private void dealWithTransferException (OsmTransferException e) { 640 640 Main.pleaseWaitDlg.currentAction.setText(tr("Transfer aborted due to error (will wait for 5 seconds):") + e.getMessage()); -
trunk/src/org/openstreetmap/josm/io/OsmTransferException.java
r784 r1169 4 4 public class OsmTransferException extends Exception { 5 5 6 7 6 public OsmTransferException() { 7 } 8 8 9 10 11 9 public OsmTransferException(String message) { 10 super(message); 11 } 12 12 13 14 15 13 public OsmTransferException(Throwable cause) { 14 super(cause); 15 } 16 16 17 18 19 17 public OsmTransferException(String message, Throwable cause) { 18 super(message, cause); 19 } 20 20 21 21 } -
trunk/src/org/openstreetmap/josm/io/OsmWriter.java
r1071 r1169 24 24 public class OsmWriter extends XmlWriter implements Visitor { 25 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 *case, not all information can be retrieved later (as example, modified state69 *is lost and id's remain 0 instead of decrementing from -1)70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 } 88 89 90 91 } 92 93 94 95 26 /** 27 * The counter for new created objects. Starting at -1 and goes down. 28 */ 29 private long newIdCounter = -1; 30 /** 31 * All newly created ids and their primitive that uses it. This is a back reference 32 * map to allow references to use the correnct primitives. 33 */ 34 public HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>(); 35 36 private final boolean osmConform; 37 private final Changeset changeset; 38 39 public abstract static class Osm implements OsmWriterInterface { 40 public void header(PrintWriter out) { 41 out.print("<osm version='"); 42 out.print(Main.pref.get("osm-server.version", "0.5")); 43 out.println("' generator='JOSM'>"); 44 } 45 public void footer(PrintWriter out) { 46 out.println("</osm>"); 47 } 48 } 49 50 // simple helper to write the object's class to the out stream 51 private Visitor typeWriteVisitor = new Visitor() { 52 public void visit(Node n) { out.print("node"); } 53 public void visit(Way w) { out.print("way"); } 54 public void visit(Relation e) { out.print("relation"); } 55 }; 56 57 /** 58 * An output writer for function output that writes everything of the given dataset into 59 * the xml 60 */ 61 public static final class All extends Osm { 62 private final DataSet ds; 63 private final boolean osmConform; 64 65 /** 66 * Construct an writer function 67 * @param osmConform <code>true</code>, if the xml should be 100% osm conform. In this 68 * case, not all information can be retrieved later (as example, modified state 69 * is lost and id's remain 0 instead of decrementing from -1) 70 */ 71 public All(DataSet ds, boolean osmConform) { 72 this.ds = ds; 73 this.osmConform = osmConform; 74 } 75 76 public void write(PrintWriter out) { 77 Visitor writer = new OsmWriter(out, osmConform, null); 78 for (Node n : ds.nodes) 79 if (shouldWrite(n)) 80 writer.visit(n); 81 for (Way w : ds.ways) 82 if (shouldWrite(w)) 83 writer.visit(w); 84 for (Relation e : ds.relations) 85 if (shouldWrite(e)) 86 writer.visit(e); 87 } 88 89 private boolean shouldWrite(OsmPrimitive osm) { 90 return osm.id != 0 || !osm.deleted; 91 } 92 93 @Override public void header(PrintWriter out) { 94 super.header(out); 95 for (DataSource s : ds.dataSources) { 96 96 // TODO: remove <bound> output after a grace period (1st October 08) 97 98 99 100 101 102 103 out.print(" <bounds minlat='" + 104 105 106 107 108 109 110 } 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 } 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 out.println("' ref='"+getUsedId(em.member)+"' role='" + 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 97 out.print(" <bound note='this tag is deprecated and only provided for backward compatiblity' box='"+ 98 s.bounds.min.lat()+","+ 99 s.bounds.min.lon()+","+ 100 s.bounds.max.lat()+","+ 101 s.bounds.max.lon()+"' "); 102 out.println("origin='"+XmlWriter.encode(s.origin)+"' />"); 103 out.print(" <bounds minlat='" + 104 s.bounds.min.lat()+"' minlon='"+ 105 s.bounds.min.lon()+"' maxlat='"+ 106 s.bounds.max.lat()+"' maxlon='"+ 107 s.bounds.max.lon()+"' "); 108 out.println("origin='"+XmlWriter.encode(s.origin)+"' />"); 109 } 110 } 111 } 112 113 /** 114 * An output writer for functino output that writes only one specific primitive into 115 * the xml 116 */ 117 public static final class Single extends Osm { 118 private final OsmPrimitive osm; 119 private final boolean osmConform; 120 private final Changeset changeset; 121 122 public Single(OsmPrimitive osm, boolean osmConform, Changeset changeset) { 123 this.osm = osm; 124 this.osmConform = osmConform; 125 this.changeset = changeset; 126 } 127 128 public void write(PrintWriter out) { 129 osm.visit(new OsmWriter(out, osmConform, changeset)); 130 } 131 } 132 133 public OsmWriter(PrintWriter out, boolean osmConform, Changeset changeset) { 134 super(out); 135 this.osmConform = osmConform; 136 this.changeset = changeset; 137 } 138 139 public void visit(Node n) { 140 if (n.incomplete) return; 141 addCommon(n, "node"); 142 out.print(" lat='"+n.coor.lat()+"' lon='"+n.coor.lon()+"'"); 143 addTags(n, "node", true); 144 } 145 146 public void visit(Way w) { 147 if (w.incomplete) return; 148 addCommon(w, "way"); 149 out.println(">"); 150 for (Node n : w.nodes) 151 out.println(" <nd ref='"+getUsedId(n)+"' />"); 152 addTags(w, "way", false); 153 } 154 155 public void visit(Relation e) { 156 if (e.incomplete) return; 157 addCommon(e, "relation"); 158 out.println(">"); 159 for (RelationMember em : e.members) { 160 out.print(" <member type='"); 161 em.member.visit(typeWriteVisitor); 162 out.println("' ref='"+getUsedId(em.member)+"' role='" + 163 XmlWriter.encode(em.role) + "' />"); 164 } 165 addTags(e, "relation", false); 166 } 167 168 169 /** 170 * Return the id for the given osm primitive (may access the usedId map) 171 */ 172 private long getUsedId(OsmPrimitive osm) { 173 if (osm.id != 0) 174 return osm.id; 175 if (usedNewIds.containsKey(osm)) 176 return usedNewIds.get(osm); 177 usedNewIds.put(osm, newIdCounter); 178 return osmConform ? 0 : newIdCounter--; 179 } 180 181 private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) { 182 if (osm.keys != null) { 183 if (tagOpen) 184 out.println(">"); 185 for (Entry<String, String> e : osm.keys.entrySet()) 186 out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) + 187 "' v='"+XmlWriter.encode(e.getValue())+ "' />"); 188 out.println(" </" + tagname + ">"); 189 } else if (tagOpen) 190 out.println(" />"); 191 else 192 out.println(" </" + tagname + ">"); 193 } 194 195 /** 196 * Add the common part as the form of the tag as well as the XML attributes 197 * id, action, user, and visible. 198 */ 199 private void addCommon(OsmPrimitive osm, String tagname) { 200 out.print(" <"+tagname+" id='"+getUsedId(osm)+"'"); 201 if (!osmConform) { 202 String action = null; 203 if (osm.deleted) 204 action = "delete"; 205 else if (osm.modified) 206 action = "modify"; 207 if (action != null) 208 out.print(" action='"+action+"'"); 209 } 210 if (osm.timestamp != null) { 211 out.print(" timestamp='"+osm.timestamp+"'"); 212 } 213 // user and visible added with 0.4 API 214 if (osm.user != null) { 215 out.print(" user='"+XmlWriter.encode(osm.user.name)+"'"); 216 } 217 out.print(" visible='"+osm.visible+"'"); 218 if (osm.version != -1) 219 out.print(" version='"+osm.version+"'"); 220 if (this.changeset != null && this.changeset.id != 0) 221 out.print(" changeset='"+this.changeset.id+"'" ); 222 } 223 223 } -
trunk/src/org/openstreetmap/josm/io/ProgressInputStream.java
r782 r1169 16 16 public class ProgressInputStream extends InputStream { 17 17 18 19 20 21 22 18 private final InputStream in; 19 private int readSoFar = 0; 20 private int lastDialogUpdate = 0; 21 private final URLConnection connection; 22 private PleaseWaitDialog pleaseWaitDlg; 23 23 24 25 26 27 28 24 public class OsmServerException extends IOException { 25 private OsmServerException(String e) { 26 super(e); 27 } 28 } 29 29 30 31 30 public ProgressInputStream(URLConnection con, PleaseWaitDialog pleaseWaitDlg) throws IOException, OsmServerException { 31 this.connection = con; 32 32 33 34 35 36 37 38 39 33 try { 34 this.in = con.getInputStream(); 35 } catch (IOException e) { 36 if (con.getHeaderField("Error") != null) 37 throw new OsmServerException(tr(con.getHeaderField("Error"))); 38 throw e; 39 } 40 40 41 42 43 44 45 46 47 48 49 50 41 int contentLength = con.getContentLength(); 42 this.pleaseWaitDlg = pleaseWaitDlg; 43 if (pleaseWaitDlg == null) 44 return; 45 if (contentLength > 0) 46 pleaseWaitDlg.progress.setMaximum(contentLength); 47 else 48 pleaseWaitDlg.progress.setMaximum(0); 49 pleaseWaitDlg.progress.setValue(0); 50 } 51 51 52 53 54 52 @Override public void close() throws IOException { 53 in.close(); 54 } 55 55 56 57 58 59 60 61 56 @Override public int read(byte[] b, int off, int len) throws IOException { 57 int read = in.read(b, off, len); 58 if (read != -1) 59 advanceTicker(read); 60 return read; 61 } 62 62 63 64 65 66 67 68 63 @Override public int read() throws IOException { 64 int read = in.read(); 65 if (read != -1) 66 advanceTicker(1); 67 return read; 68 } 69 69 70 71 72 73 74 75 76 70 /** 71 * Increase ticker (progress counter and displayed text) by the given amount. 72 * @param amount 73 */ 74 private void advanceTicker(int amount) { 75 if (pleaseWaitDlg == null) 76 return; 77 77 78 79 78 if (pleaseWaitDlg.progress.getMaximum() == 0 && connection.getContentLength() != -1) 79 pleaseWaitDlg.progress.setMaximum(connection.getContentLength()); 80 80 81 81 readSoFar += amount; 82 82 83 84 85 86 87 83 if (readSoFar / 1024 != lastDialogUpdate) { 84 lastDialogUpdate++; 85 String progStr = " "+readSoFar/1024+"/"; 86 progStr += (pleaseWaitDlg.progress.getMaximum()==0) ? "??? KB" : (pleaseWaitDlg.progress.getMaximum()/1024)+" KB"; 87 pleaseWaitDlg.progress.setValue(readSoFar); 88 88 89 90 91 92 93 94 95 96 97 89 String cur = pleaseWaitDlg.currentAction.getText(); 90 int i = cur.indexOf(' '); 91 if (i != -1) 92 cur = cur.substring(0, i) + progStr; 93 else 94 cur += progStr; 95 pleaseWaitDlg.currentAction.setText(cur); 96 } 97 } 98 98 } -
trunk/src/org/openstreetmap/josm/io/XmlWriter.java
r627 r1169 10 10 /** 11 11 * Helper class to use for xml outputting classes. 12 * 12 * 13 13 * @author imi 14 14 */ 15 15 public class XmlWriter { 16 16 17 18 19 20 21 22 23 24 25 17 /** 18 * The interface to write the data into an Osm stream 19 * @author immanuel.scholz 20 */ 21 public static interface OsmWriterInterface { 22 void header(PrintWriter out); 23 void write(PrintWriter out); 24 void footer(PrintWriter out); 25 } 26 26 27 27 28 29 30 28 protected XmlWriter(PrintWriter out) { 29 this.out = out; 30 } 31 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 32 /** 33 * Encode the given string in XML1.0 format. 34 * Optimized to fast pass strings that don't need encoding (normal case). 35 */ 36 public static String encode(String unencoded) { 37 StringBuilder buffer = null; 38 for (int i = 0; i < unencoded.length(); ++i) { 39 String encS = XmlWriter.encoding.get(unencoded.charAt(i)); 40 if (encS != null) { 41 if (buffer == null) 42 buffer = new StringBuilder(unencoded.substring(0,i)); 43 buffer.append(encS); 44 } else if (buffer != null) 45 buffer.append(unencoded.charAt(i)); 46 } 47 return (buffer == null) ? unencoded : buffer.toString(); 48 } 49 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 50 /** 51 * Write the header and start tag, then call the runnable to add all real tags and finally 52 * "closes" the xml by writing the footer. 53 */ 54 public static void output(OutputStream outStream, OsmWriterInterface outputWriter) { 55 PrintWriter out; 56 try { 57 out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8")); 58 } catch (UnsupportedEncodingException e) { 59 throw new RuntimeException(e); 60 } 61 out.println("<?xml version='1.0' encoding='UTF-8'?>"); 62 outputWriter.header(out); 63 outputWriter.write(out); 64 outputWriter.footer(out); 65 out.flush(); 66 out.close(); 67 } 68 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 69 /** 70 * The output writer to save the values to. 71 */ 72 protected final PrintWriter out; 73 final private static HashMap<Character, String> encoding = new HashMap<Character, String>(); 74 static { 75 encoding.put('<', "<"); 76 encoding.put('>', ">"); 77 encoding.put('"', """); 78 encoding.put('\'', "'"); 79 encoding.put('&', "&"); 80 encoding.put('\n', "
"); 81 encoding.put('\r', "
"); 82 encoding.put('\t', "	"); 83 } 84 84 } -
trunk/src/org/openstreetmap/josm/plugins/Plugin.java
r873 r1169 24 24 * 25 25 * The actual implementation of this class is optional, as all functions will be called 26 * via reflection. This is to be able to change this interface without the need of 26 * via reflection. This is to be able to change this interface without the need of 27 27 * recompiling or even breaking the plugins. If your class does not provide a 28 28 * function here (or does provide a function with a mismatching signature), it will not … … 32 32 * are provided (you can register yourself to more callbacks in your plugin class 33 33 * constructor). 34 * 34 * 35 35 * Subclassing Plugin and overriding some functions makes it easy for you to keep sync 36 36 * with the correct actual plugin architecture of JOSM. … … 40 40 public abstract class Plugin { 41 41 42 43 44 45 46 47 48 49 42 /** 43 * This is the info available for this plugin. You can access this from your 44 * constructor. 45 * 46 * (The actual implementation to request the info from a static variable 47 * is a bit hacky, but it works). 48 */ 49 public final PluginInformation info = PluginInformation.currentPluginInitialization; 50 50 51 52 53 54 55 56 51 /** 52 * @return The directory for the plugin to store all kind of stuff. 53 */ 54 public final String getPluginDir() { 55 return new File(Main.pref.getPluginsDirFile(), info.name).getPath(); 56 } 57 57 58 59 60 61 62 63 58 /** 59 * Called after Main.mapFrame is initalized. (After the first data is loaded). 60 * You can use this callback to tweak the newFrame to your needs, as example install 61 * an alternative Painter. 62 */ 63 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {} 64 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 65 /** 66 * Called in the preferences dialog to create a preferences page for the plugin, 67 * if any available. 68 */ 69 public PreferenceSetting getPreferenceSetting() { return null; } 70 71 /** 72 * Called in the download dialog to give the plugin a chance to modify the list 73 * of bounding box selectors. 74 */ 75 public void addDownloadSelection(List<DownloadSelection> list) {} 76 77 /** 78 * Copies the ressource 'from' to the file in the plugin directory named 'to'. 79 */ 80 public void copy(String from, String to) throws FileNotFoundException, IOException { 81 String pluginDirName = Main.pref.getPreferencesDir()+"plugins/"+info.name+"/"; 82 82 File pluginDir = new File(pluginDirName); 83 83 if (!pluginDir.exists()) 84 84 pluginDir.mkdirs(); 85 85 FileOutputStream out = new FileOutputStream(pluginDirName+to); 86 86 InputStream in = getClass().getResourceAsStream(from); 87 87 byte[] buffer = new byte[8192]; 88 88 for(int len = in.read(buffer); len > 0; len = in.read(buffer)) 89 89 out.write(buffer, 0, len); 90 90 in.close(); 91 91 out.close(); -
trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java
r1073 r1169 1 1 //License: GPL. Copyright 2007 by Immanuel Scholz and others 2 2 /** 3 * 3 * 4 4 */ 5 5 package org.openstreetmap.josm.plugins; … … 34 34 public class PluginDownloader { 35 35 36 37 38 39 36 private static final class UpdateTask extends PleaseWaitRunnable { 37 private final Collection<PluginDescription> toUpdate; 38 private String errors = ""; 39 private int count = 0; 40 40 41 42 43 44 41 private UpdateTask(Collection<PluginDescription> toUpdate) { 42 super(tr("Update Plugins")); 43 this.toUpdate = toUpdate; 44 } 45 45 46 47 48 46 @Override protected void cancel() { 47 finish(); 48 } 49 49 50 51 52 53 54 55 50 @Override protected void finish() { 51 if (errors.length() > 0) 52 JOptionPane.showMessageDialog(Main.parent, tr("There were problems with the following plugins:\n\n {0}",errors)); 53 else 54 JOptionPane.showMessageDialog(Main.parent, trn("{0} Plugin successfully updated. Please restart JOSM.", "{0} Plugins successfully updated. Please restart JOSM.", count, count)); 55 } 56 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 57 @Override protected void realRun() throws SAXException, IOException { 58 File pluginDir = Main.pref.getPluginsDirFile(); 59 if (!pluginDir.exists()) 60 pluginDir.mkdirs(); 61 for (PluginDescription d : toUpdate) { 62 File pluginFile = new File(pluginDir, d.name + ".jar.new"); 63 if (download(d.resource, pluginFile)) 64 count++; 65 else 66 errors += d.name + "\n"; 67 } 68 PluginDownloader.moveUpdatedPlugins(); 69 } 70 } 71 71 72 72 private static final Pattern wiki = Pattern.compile("^</td></tr><tr><td><a class=\"ext-link\" href=\"([^\"]*)\"><span class=\"icon\">([^<]*)</span></a></td><td>([^<]*)</td><td>([^<].*)</td><td>(.*)"); 73 73 74 74 private final static String[] pluginSites = {"http://josm.openstreetmap.de/wiki/Plugins"}; 75 75 76 77 78 76 public static Collection<String> getSites() { 77 return Main.pref.getCollection("pluginmanager.sites", Arrays.asList(pluginSites)); 78 } 79 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 80 public static int downloadDescription() { 81 int count = 0; 82 for (String site : getSites()) { 83 try { 84 BufferedReader r = new BufferedReader(new InputStreamReader(new URL(site).openStream())); 85 CharSequence txt; 86 if (site.toLowerCase().endsWith(".xml")) 87 txt = readXml(r); 88 else 89 txt = readWiki(r); 90 r.close(); 91 new File(Main.pref.getPreferencesDir()+"plugins").mkdir(); 92 FileWriter out = new FileWriter(new File(Main.pref 93 .getPluginsDirFile(), count + "-site-" 94 + site.replaceAll("[/:\\\\ <>|]", "_") + ".xml")); 95 out.append(txt); 96 out.close(); 97 count++; 98 } catch (IOException x) { 99 } 100 } 101 return count; 102 } 103 103 104 105 106 107 108 109 104 private static CharSequence readXml(BufferedReader r) throws IOException { 105 StringBuilder b = new StringBuilder(); 106 for (String line = r.readLine(); line != null; line = r.readLine()) 107 b.append(line+"\n"); 108 return b; 109 } 110 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 111 private static CharSequence readWiki(BufferedReader r) throws IOException { 112 StringBuilder b = new StringBuilder("<plugins>\n"); 113 for (String line = r.readLine(); line != null; line = r.readLine()) { 114 Matcher m = wiki.matcher(line); 115 if (!m.matches()) 116 continue; 117 b.append(" <plugin>\n"); 118 b.append(" <name>"+escape(m.group(2))+"</name>\n"); 119 b.append(" <resource>"+escape(m.group(1))+"</resource>\n"); 120 b.append(" <author>"+escape(m.group(3))+"</author>\n"); 121 b.append(" <description>"+escape(m.group(4))+"</description>\n"); 122 b.append(" <version>"+escape(m.group(5))+"</version>\n"); 123 b.append(" </plugin>\n"); 124 } 125 b.append("</plugins>\n"); 126 return b; 127 } 128 128 129 130 131 129 private static String escape(String s) { 130 return s.replaceAll("<", "<").replaceAll(">", ">"); 131 } 132 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 133 public static boolean downloadPlugin(PluginDescription pd) { 134 File file = new File(Main.pref.getPluginsDirFile(), pd.name + ".jar"); 135 if (!download(pd.resource, file)) { 136 JOptionPane.showMessageDialog(Main.parent, tr("Could not download plugin: {0} from {1}", pd.name, pd.resource)); 137 } else { 138 try { 139 PluginInformation.findPlugin(pd.name); 140 return true; 141 } catch (Exception e) { 142 e.printStackTrace(); 143 JOptionPane.showMessageDialog(Main.parent, tr("The plugin {0} seem to be broken or could not be downloaded automatically.", pd.name)); 144 } 145 } 146 if (file.exists()) 147 file.delete(); 148 return false; 149 } 150 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 151 private static boolean download(String url, File file) { 152 try { 153 InputStream in = new URL(url).openStream(); 154 OutputStream out = new FileOutputStream(file); 155 byte[] buffer = new byte[8192]; 156 for (int read = in.read(buffer); read != -1; read = in.read(buffer)) 157 out.write(buffer, 0, read); 158 out.close(); 159 in.close(); 160 return true; 161 } catch (MalformedURLException e) { 162 e.printStackTrace(); 163 } catch (FileNotFoundException e) { 164 e.printStackTrace(); 165 } catch (IOException e) { 166 e.printStackTrace(); 167 } 168 return false; 169 } 170 170 171 172 173 174 175 176 177 boolean ok = true; 178 179 180 181 171 public static void update(Collection<PluginDescription> update) { 172 Main.worker.execute(new UpdateTask(update)); 173 } 174 175 public static boolean moveUpdatedPlugins() { 176 File pluginDir = Main.pref.getPluginsDirFile(); 177 boolean ok = true; 178 if (pluginDir.exists() && pluginDir.isDirectory() && pluginDir.canWrite()) { 179 final File[] files = pluginDir.listFiles(new FilenameFilter() { 180 public boolean accept(File dir, String name) { 181 return name.endsWith(".new"); 182 182 }}); 183 184 185 186 187 188 189 190 183 for (File updatedPlugin : files) { 184 final String filePath = updatedPlugin.getPath(); 185 File plugin = new File(filePath.substring(0, filePath.length() - 4)); 186 ok = (plugin.delete() || !plugin.exists()) && updatedPlugin.renameTo(plugin) && ok; 187 } 188 } 189 return ok; 190 } 191 191 } -
trunk/src/org/openstreetmap/josm/plugins/PluginException.java
r1116 r1169 8 8 * and there is no particular reason to use this within the plugin itself (although there 9 9 * is also no reason against this.. ;) 10 * 10 * 11 11 * @author Immanuel.Scholz 12 12 */ 13 13 public class PluginException extends RuntimeException { 14 15 14 public final PluginProxy plugin; 15 public final String name; 16 16 17 18 19 20 17 public PluginException(PluginProxy plugin, String name, Throwable cause) { 18 super(tr("An error occured in plugin {0}", name), cause); 19 this.plugin = plugin; 20 this.name = name; 21 21 } 22 22 } -
trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java
r1073 r1169 29 29 */ 30 30 public class PluginInformation { 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 * 49 * If you think this is hacky, you are probably right. But it is 50 * convinient anyway ;-) 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 * 160 161 162 163 164 165 166 167 *<li>If the environment variable ALLUSERSPROFILE and APPDATA exist, look in 168 * ALLUSERSPROFILE/<the last stuff from APPDATA>/JOSM/plugins. 169 * (*sic* There is no easy way under Windows to get the All User's application 170 171 *<li>Finally, look in some typical unix paths:<ul> 172 173 174 175 176 * 177 178 179 180 * ignore them. 181 * 182 183 * (only the manifest is extracted). In the classloader-case, the class is 184 185 186 187 188 *was nowhere to be found.189 190 191 192 193 194 195 196 31 32 public final File file; 33 public final String name; 34 public final String mainversion; 35 public final String className; 36 public final String description; 37 public final boolean early; 38 public final String author; 39 public final int stage; 40 public final String version; 41 public final List<URL> libraries = new LinkedList<URL>(); 42 43 public final Map<String, String> attr = new TreeMap<String, String>(); 44 45 /** 46 * Used in the Plugin constructor to make the information of the plugin 47 * that is currently initializing available. 48 * 49 * If you think this is hacky, you are probably right. But it is 50 * convinient anyway ;-) 51 */ 52 static PluginInformation currentPluginInitialization = null; 53 54 /** 55 * @param file the plugin jar file. 56 */ 57 public PluginInformation(File file) { 58 this(file, file.getName().substring(0, file.getName().length()-4), null); 59 } 60 61 public PluginInformation(File file, String name, InputStream manifestStream) { 62 this.name = name; 63 this.file = file; 64 try { 65 Manifest manifest; 66 JarInputStream jar = null; 67 if (file != null) { 68 jar = new JarInputStream(new FileInputStream(file)); 69 manifest = jar.getManifest(); 70 if (manifest == null) 71 throw new IOException(file+" contains no manifest."); 72 } else { 73 manifest = new Manifest(); 74 manifest.read(manifestStream); 75 } 76 if (manifest != null) { 77 Attributes attr = manifest.getMainAttributes(); 78 className = attr.getValue("Plugin-Class"); 79 description = attr.getValue("Plugin-Description"); 80 early = Boolean.parseBoolean(attr.getValue("Plugin-Early")); 81 String stageStr = attr.getValue("Plugin-Stage"); 82 stage = stageStr == null ? 50 : Integer.parseInt(stageStr); 83 version = attr.getValue("Plugin-Version"); 84 mainversion = attr.getValue("Plugin-Mainversion"); 85 author = attr.getValue("Author"); 86 87 String classPath = attr.getValue(Attributes.Name.CLASS_PATH); 88 if (classPath != null) { 89 for (String entry : classPath.split(" ")) { 90 File entryFile; 91 if (new File(entry).isAbsolute()) { 92 entryFile = new File(entry); 93 } else { 94 entryFile = new File(file.getParent(), entry); 95 } 96 97 libraries.add(fileToURL(entryFile)); 98 } 99 } 100 for (Object o : attr.keySet()) 101 this.attr.put(o.toString(), attr.getValue(o.toString())); 102 } else { 103 // resource-only plugin 104 className = null; 105 mainversion = null; 106 description = tr("unknown"); 107 early = false; 108 stage = 50; 109 version = null; 110 author = null; 111 } 112 if (file != null) 113 libraries.add(0, fileToURL(file)); 114 115 if (jar != null) 116 jar.close(); 117 } catch (IOException e) { 118 throw new PluginException(null, name, e); 119 } 120 } 121 122 /** 123 * Load and instantiate the plugin 124 */ 125 public PluginProxy load(Class<?> klass) { 126 try { 127 currentPluginInitialization = this; 128 return new PluginProxy(klass.newInstance(), this); 129 } catch (Exception e) { 130 throw new PluginException(null, name, e); 131 } 132 } 133 134 /** 135 * Load the class of the plugin 136 */ 137 public Class<?> loadClass(ClassLoader classLoader) { 138 if (className == null) 139 return null; 140 try { 141 Class<?> realClass = Class.forName(className, true, classLoader); 142 return realClass; 143 } catch (Exception e) { 144 throw new PluginException(null, name, e); 145 } 146 } 147 148 public static URL fileToURL(File f) { 149 try { 150 return f.toURI().toURL(); 151 } catch (MalformedURLException ex) { 152 return null; 153 } 154 } 155 156 /** 157 * Try to find a plugin after some criterias. Extract the plugin-information 158 * from the plugin and return it. The plugin is searched in the following way: 159 * 160 *<li>first look after an MANIFEST.MF in the package org.openstreetmap.josm.plugins.<plugin name> 161 * (After removing all fancy characters from the plugin name). 162 * If found, the plugin is loaded using the bootstrap classloader. 163 *<li>If not found, look for a jar file in the user specific plugin directory 164 * (~/.josm/plugins/<plugin name>.jar) 165 *<li>If not found and the environment variable JOSM_RESSOURCES + "/plugins/" exist, look there. 166 *<li>Try for the java property josm.ressources + "/plugins/" (set via java -Djosm.plugins.path=...) 167 *<li>If the environment variable ALLUSERSPROFILE and APPDATA exist, look in 168 * ALLUSERSPROFILE/<the last stuff from APPDATA>/JOSM/plugins. 169 * (*sic* There is no easy way under Windows to get the All User's application 170 * directory) 171 *<li>Finally, look in some typical unix paths:<ul> 172 * <li>/usr/local/share/josm/plugins/ 173 * <li>/usr/local/lib/josm/plugins/ 174 * <li>/usr/share/josm/plugins/ 175 * <li>/usr/lib/josm/plugins/ 176 * 177 * If a plugin class or jar file is found earlier in the list but seem not to 178 * be working, an PluginException is thrown rather than continuing the search. 179 * This is so JOSM can detect broken user-provided plugins and do not go silently 180 * ignore them. 181 * 182 * The plugin is not initialized. If the plugin is a .jar file, it is not loaded 183 * (only the manifest is extracted). In the classloader-case, the class is 184 * bootstraped (e.g. static {} - declarations will run. However, nothing else is done. 185 * 186 * @param pluginName The name of the plugin (in all lowercase). E.g. "lang-de" 187 * @return Information about the plugin or <code>null</code>, if the plugin 188 * was nowhere to be found. 189 * @throws PluginException In case of broken plugins. 190 */ 191 public static PluginInformation findPlugin(String pluginName) throws PluginException { 192 String name = pluginName; 193 name = name.replaceAll("[-. ]", ""); 194 InputStream manifestStream = PluginInformation.class.getResourceAsStream("/org/openstreetmap/josm/plugins/"+name+"/MANIFEST.MF"); 195 if (manifestStream != null) 196 return new PluginInformation(null, pluginName, manifestStream); 197 197 198 198 Collection<String> locations = getPluginLocations(); 199 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 } 217 218 219 220 221 * 222 223 224 * 225 226 227 228 229 230 231 232 233 200 for (String s : locations) { 201 File pluginFile = new File(s, pluginName + ".jar"); 202 if (pluginFile.exists()) { 203 PluginInformation info = new PluginInformation(pluginFile); 204 return info; 205 } 206 } 207 return null; 208 } 209 210 public static Collection<String> getPluginLocations() { 211 Collection<String> locations = Main.pref.getAllPossiblePreferenceDirs(); 212 Collection<String> all = new ArrayList<String>(locations.size()); 213 for (String s : locations) 214 all.add(s+"plugins"); 215 return all; 216 } 217 218 219 /** 220 * Return information about a loaded plugin. 221 * 222 * Note that if you call this in your plugins bootstrap, you may get <code>null</code> if 223 * the plugin requested is not loaded yet. 224 * 225 * @return The PluginInformation to a specific plugin, but only if the plugin is loaded. 226 * If it is not loaded, <code>null</code> is returned. 227 */ 228 public static PluginInformation getLoaded(String pluginName) { 229 for (PluginProxy p : Main.plugins) 230 if (p.info.name.equals(pluginName)) 231 return p.info; 232 return null; 233 } 234 234 } 235 235 -
trunk/src/org/openstreetmap/josm/plugins/PluginProxy.java
r627 r1169 18 18 public class PluginProxy extends Plugin { 19 19 20 21 20 public final Object plugin; 21 public final PluginInformation info; 22 22 23 24 25 23 public PluginProxy(Object plugin, PluginInformation info) { 24 this.plugin = plugin; 25 this.info = info; 26 26 } 27 27 28 29 30 28 @Override public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 29 try { 30 plugin.getClass().getMethod("mapFrameInitialized", MapFrame.class, MapFrame.class).invoke(plugin, oldFrame, newFrame); 31 31 } catch (NoSuchMethodException e) { 32 32 } catch (Exception e) { 33 33 throw new PluginException(this, info.name, e); 34 34 } 35 35 } 36 36 37 38 39 40 41 42 43 44 37 @Override public PreferenceSetting getPreferenceSetting() { 38 try { 39 return (PreferenceSetting)plugin.getClass().getMethod("getPreferenceSetting").invoke(plugin); 40 } catch (NoSuchMethodException e) { 41 return null; 42 } catch (Exception e) { 43 throw new PluginException(this, info.name, e); 44 } 45 45 } 46 47 48 49 50 51 52 53 54 55 46 47 @Override public void addDownloadSelection(List<DownloadSelection> list) { 48 try { 49 plugin.getClass().getMethod("addDownloadSelection", List.class).invoke(plugin, list); 50 } catch (NoSuchMethodException e) { 51 // ignore 52 } catch (Exception e) { 53 throw new PluginException(this, info.name, e); 54 } 55 } 56 56 } -
trunk/src/org/openstreetmap/josm/tools/AudioPlayer.java
r891 r1169 18 18 /** 19 19 * Creates and controls a separate audio player thread. 20 * 20 * 21 21 * @author David Earl <david@frankieandshadow.com> 22 22 * … … 24 24 public class AudioPlayer extends Thread { 25 25 26 27 28 private enum State { INITIALIZING, NOTPLAYING, PLAYING, PAUSED, INTERRUPTED } 29 26 private static AudioPlayer audioPlayer = null; 27 28 private enum State { INITIALIZING, NOTPLAYING, PLAYING, PAUSED, INTERRUPTED } 29 private State state; 30 30 private enum Command { PLAY, PAUSE } 31 31 private enum Result { WAITING, OK, FAILED } … … 33 33 private double leadIn; // seconds 34 34 private double calibration; // ratio of purported duration of samples to true duration 35 36 private double bytesPerSecond; 37 38 39 40 41 * Passes information from the control thread to the playing thread 42 43 44 45 46 47 48 49 50 51 52 * Called to execute the commands in the other thread 53 54 55 56 57 this.speed = speed; 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 * Plays a WAV audio file from the beginning. See also the variant which doesn't 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 * gets the singleton object, and if this is the first time, creates it along with 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 AudioFormataudioFormat = null;224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 case PLAY: 266 267 268 if (playingUrl != command.url() || 269 stateChange != State.PAUSED || 270 offset != 0.0) 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 /* skip doesn't seem to want to skip big chunks, so 291 * reduce it to smaller ones 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 audioFormat = new AudioFormat(audioFormat.getEncoding(), 307 audioFormat.getSampleRate() * (float) (speed * calibration), 308 audioFormat.getSampleSizeInBits(), 309 audioFormat.getChannels(), 310 audioFormat.getFrameSize(), 311 audioFormat.getFrameRate() * (float) (speed * calibration), 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 JOptionPane.showMessageDialog(Main.parent, 336 337 338 35 private double position; // seconds 36 private double bytesPerSecond; 37 private static long chunk = 4000; /* bytes */ 38 private double speed = 1.0; 39 40 /** 41 * Passes information from the control thread to the playing thread 42 */ 43 private class Execute { 44 private Command command; 45 private Result result; 46 private Exception exception; 47 private URL url; 48 private double offset; // seconds 49 private double speed; // ratio 50 51 /* 52 * Called to execute the commands in the other thread 53 */ 54 protected void play(URL url, double offset, double speed) throws Exception { 55 this.url = url; 56 this.offset = offset; 57 this.speed = speed; 58 command = Command.PLAY; 59 result = Result.WAITING; 60 send(); 61 } 62 protected void pause() throws Exception { 63 command = Command.PAUSE; 64 send(); 65 } 66 private void send() throws Exception { 67 result = Result.WAITING; 68 interrupt(); 69 while (result == Result.WAITING) { sleep(10); /* yield(); */ } 70 if (result == Result.FAILED) { throw exception; } 71 } 72 private void possiblyInterrupt() throws InterruptedException { 73 if (interrupted() || result == Result.WAITING) 74 throw new InterruptedException(); 75 } 76 protected void failed (Exception e) { 77 exception = e; 78 result = Result.FAILED; 79 state = State.NOTPLAYING; 80 } 81 protected void ok (State newState) { 82 result = Result.OK; 83 state = newState; 84 } 85 protected double offset() { 86 return offset; 87 } 88 protected double speed() { 89 return speed; 90 } 91 protected URL url() { 92 return url; 93 } 94 protected Command command() { 95 return command; 96 } 97 } 98 99 private Execute command; 100 101 /** 102 * Plays a WAV audio file from the beginning. See also the variant which doesn't 103 * start at the beginning of the stream 104 * @param url The resource to play, which must be a WAV file or stream 105 * @throws audio fault exception, e.g. can't open stream, unhandleable audio format 106 */ 107 public static void play(URL url) throws Exception { 108 AudioPlayer.get().command.play(url, 0.0, 1.0); 109 } 110 111 /** 112 * Plays a WAV audio file from a specified position. 113 * @param url The resource to play, which must be a WAV file or stream 114 * @param seconds The number of seconds into the audio to start playing 115 * @throws audio fault exception, e.g. can't open stream, unhandleable audio format 116 */ 117 public static void play(URL url, double seconds) throws Exception { 118 AudioPlayer.get().command.play(url, seconds, 1.0); 119 } 120 121 /** 122 * Plays a WAV audio file from a specified position at variable speed. 123 * @param url The resource to play, which must be a WAV file or stream 124 * @param seconds The number of seconds into the audio to start playing 125 * @param speed Rate at which audio playes (1.0 = real time, > 1 is faster) 126 * @throws audio fault exception, e.g. can't open stream, unhandleable audio format 127 */ 128 public static void play(URL url, double seconds, double speed) throws Exception { 129 AudioPlayer.get().command.play(url, seconds, speed); 130 } 131 132 /** 133 * Pauses the currently playing audio stream. Does nothing if nothing playing. 134 * @throws audio fault exception, e.g. can't open stream, unhandleable audio format 135 */ 136 public static void pause() throws Exception { 137 AudioPlayer.get().command.pause(); 138 } 139 140 /** 141 * To get the Url of the playing or recently played audio. 142 * @return url - could be null 143 */ 144 public static URL url() { 145 return AudioPlayer.get().playingUrl; 146 } 147 148 /** 149 * Whether or not we are paused. 150 * @return boolean whether or not paused 151 */ 152 public static boolean paused() { 153 return AudioPlayer.get().state == State.PAUSED; 154 } 155 156 /** 157 * Whether or not we are playing. 158 * @return boolean whether or not playing 159 */ 160 public static boolean playing() { 161 return AudioPlayer.get().state == State.PLAYING; 162 } 163 164 /** 165 * How far we are through playing, in seconds. 166 * @return double seconds 167 */ 168 public static double position() { 169 return AudioPlayer.get().position; 170 } 171 172 /** 173 * Speed at which we will play. 174 * @return double, speed multiplier 175 */ 176 public static double speed() { 177 return AudioPlayer.get().speed; 178 } 179 180 /** 181 * gets the singleton object, and if this is the first time, creates it along with 182 * the thread to support audio 183 */ 184 private static AudioPlayer get() { 185 if (audioPlayer != null) 186 return audioPlayer; 187 try { 188 audioPlayer = new AudioPlayer(); 189 return audioPlayer; 190 } catch (Exception ex) { 191 return null; 192 } 193 } 194 195 private AudioPlayer() { 196 state = State.INITIALIZING; 197 command = new Execute(); 198 playingUrl = null; 199 try { 200 leadIn = Double.parseDouble(Main.pref.get("audio.leadin", "1.0" /* default, seconds */)); 201 } catch (NumberFormatException e) { 202 leadIn = 1.0; // failed to parse 203 } 204 try { 205 calibration = Double.parseDouble(Main.pref.get("audio.calibration", "1.0" /* default, ratio */)); 206 } catch (NumberFormatException e) { 207 calibration = 1.0; // failed to parse 208 } 209 start(); 210 while (state == State.INITIALIZING) { yield(); } 211 } 212 213 /** 214 * Starts the thread to actually play the audio, per Thread interface 215 * Not to be used as public, though Thread interface doesn't allow it to be made private 216 */ 217 @Override public void run() { 218 /* code running in separate thread */ 219 220 playingUrl = null; 221 AudioInputStream audioInputStream = null; 222 SourceDataLine audioOutputLine = null; 223 AudioFormat audioFormat = null; 224 byte[] abData = new byte[(int)chunk]; 225 226 for (;;) { 227 try { 228 switch (state) { 229 case INITIALIZING: 230 // we're ready to take interrupts 231 state = State.NOTPLAYING; 232 break; 233 case NOTPLAYING: 234 case PAUSED: 235 sleep(200); 236 break; 237 case PLAYING: 238 command.possiblyInterrupt(); 239 for(;;) { 240 int nBytesRead = 0; 241 nBytesRead = audioInputStream.read(abData, 0, abData.length); 242 position += nBytesRead / bytesPerSecond; 243 command.possiblyInterrupt(); 244 if (nBytesRead < 0) { break; } 245 audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten 246 command.possiblyInterrupt(); 247 } 248 // end of audio, clean up 249 audioOutputLine.drain(); 250 audioOutputLine.close(); 251 audioOutputLine = null; 252 audioInputStream.close(); 253 audioInputStream = null; 254 playingUrl = null; 255 state = State.NOTPLAYING; 256 command.possiblyInterrupt(); 257 break; 258 } 259 } catch (InterruptedException e) { 260 interrupted(); // just in case we get an interrupt 261 State stateChange = state; 262 state = State.INTERRUPTED; 263 try { 264 switch (command.command()) { 265 case PLAY: 266 double offset = command.offset(); 267 speed = command.speed(); 268 if (playingUrl != command.url() || 269 stateChange != State.PAUSED || 270 offset != 0.0) 271 { 272 if (audioInputStream != null) { 273 audioInputStream.close(); 274 audioInputStream = null; 275 } 276 playingUrl = command.url(); 277 audioInputStream = AudioSystem.getAudioInputStream(playingUrl); 278 audioFormat = audioInputStream.getFormat(); 279 long nBytesRead = 0; 280 position = 0.0; 281 offset -= leadIn; 282 double calibratedOffset = offset * calibration; 283 bytesPerSecond = audioFormat.getFrameRate() /* frames per second */ 284 * audioFormat.getFrameSize() /* bytes per frame */; 285 if (speed * bytesPerSecond > 256000.0) 286 speed = 256000 / bytesPerSecond; 287 if (calibratedOffset > 0.0) { 288 long bytesToSkip = (long)( 289 calibratedOffset /* seconds (double) */ * bytesPerSecond); 290 /* skip doesn't seem to want to skip big chunks, so 291 * reduce it to smaller ones 292 */ 293 // audioInputStream.skip(bytesToSkip); 294 while (bytesToSkip > chunk) { 295 nBytesRead = audioInputStream.skip(chunk); 296 if (nBytesRead <= 0) 297 throw new IOException(tr("This is after the end of the recording")); 298 bytesToSkip -= nBytesRead; 299 } 300 if (bytesToSkip > 0) 301 audioInputStream.skip(bytesToSkip); 302 position = offset; 303 } 304 if (audioOutputLine != null) 305 audioOutputLine.close(); 306 audioFormat = new AudioFormat(audioFormat.getEncoding(), 307 audioFormat.getSampleRate() * (float) (speed * calibration), 308 audioFormat.getSampleSizeInBits(), 309 audioFormat.getChannels(), 310 audioFormat.getFrameSize(), 311 audioFormat.getFrameRate() * (float) (speed * calibration), 312 audioFormat.isBigEndian()); 313 DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); 314 audioOutputLine = (SourceDataLine) AudioSystem.getLine(info); 315 audioOutputLine.open(audioFormat); 316 audioOutputLine.start(); 317 } 318 stateChange = State.PLAYING; 319 break; 320 case PAUSE: 321 stateChange = State.PAUSED; 322 break; 323 } 324 command.ok(stateChange); 325 } catch (Exception startPlayingException) { 326 command.failed(startPlayingException); // sets state 327 } 328 } catch (Exception e) { 329 state = State.NOTPLAYING; 330 } 331 } 332 } 333 334 public static void audioMalfunction(Exception ex) { 335 JOptionPane.showMessageDialog(Main.parent, 336 "<html><p>" + tr(ex.getMessage()) + "</p></html>", 337 tr("Error playing sound"), JOptionPane.ERROR_MESSAGE); 338 } 339 339 } -
trunk/src/org/openstreetmap/josm/tools/AutoCompleteComboBox.java
r741 r1169 17 17 public class AutoCompleteComboBox extends JComboBox { 18 18 19 20 21 22 23 24 25 26 19 /** 20 * Auto-complete a JComboBox. 21 * 22 * Inspired by http://www.orbital-computer.de/JComboBox/ 23 */ 24 private class AutoCompleteComboBoxDocument extends PlainDocument { 25 private JComboBox comboBox; 26 private boolean selecting = false; 27 27 28 29 30 28 public AutoCompleteComboBoxDocument(final JComboBox comboBox) { 29 this.comboBox = comboBox; 30 } 31 31 32 33 34 35 36 32 @Override public void remove(int offs, int len) throws BadLocationException { 33 if (selecting) 34 return; 35 super.remove(offs, len); 36 } 37 37 38 39 40 41 42 38 @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { 39 if(selecting || (offs == 0 && str.equals(getText(0, getLength())))) 40 return; 41 boolean initial = (offs == 0 && getLength() == 0 && str.length() > 1); 42 super.insertString(offs, str, a); 43 43 44 45 46 47 48 44 // return immediately when selecting an item 45 // Note: this is done after calling super method because we need 46 // ActionListener informed 47 if (selecting) 48 return; 49 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 50 int size = getLength(); 51 int start = offs+str.length(); 52 int end = start; 53 String curText = getText(0, size); 54 // lookup and select a matching item 55 Object item = lookupItem(curText); 56 setSelectedItem(item); 57 if(initial) 58 start = 0; 59 if (item != null) { 60 String newText = item.toString(); 61 if(!newText.equals(curText)) 62 { 63 selecting = true; 64 super.remove(0, size); 65 super.insertString(0, newText, a); 66 selecting = false; 67 start = size; 68 end = getLength(); 69 } 70 } 71 JTextComponent editor = (JTextComponent)comboBox.getEditor().getEditorComponent(); 72 editor.setSelectionStart(start); 73 editor.setSelectionEnd(end); 74 } 75 75 76 77 78 79 80 76 private void setSelectedItem(Object item) { 77 selecting = true; 78 comboBox.setSelectedItem(item); 79 selecting = false; 80 } 81 81 82 83 84 85 86 87 88 89 90 91 82 private Object lookupItem(String pattern) { 83 ComboBoxModel model = comboBox.getModel(); 84 for (int i = 0, n = model.getSize(); i < n; i++) { 85 Object currentItem = model.getElementAt(i); 86 if (currentItem.toString().startsWith(pattern)) 87 return currentItem; 88 } 89 return null; 90 } 91 } 92 92 93 94 95 96 93 public AutoCompleteComboBox() { 94 JTextComponent editor = (JTextComponent) this.getEditor().getEditorComponent(); 95 editor.setDocument(new AutoCompleteComboBoxDocument(this)); 96 } 97 97 98 99 100 101 102 103 104 98 public void setPossibleItems(Collection<String> elems) { 99 DefaultComboBoxModel model = (DefaultComboBoxModel)this.getModel(); 100 Object oldValue = this.getEditor().getItem(); 101 model.removeAllElements(); 102 for (String elem : elems) model.addElement(elem); 103 this.getEditor().setItem(oldValue); 104 } 105 105 } -
trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java
r1032 r1169 39 39 public final class BugReportExceptionHandler implements Thread.UncaughtExceptionHandler { 40 40 41 42 43 44 45 46 47 48 49 50 51 41 public void uncaughtException(Thread t, Throwable e) { 42 e.printStackTrace(); 43 if (Main.parent != null) { 44 if (e instanceof OutOfMemoryError) { 45 // do not translate the string, as translation may raise an exception 46 JOptionPane.showMessageDialog(Main.parent, "JOSM is out of memory. " + 47 "Strange things may happen.\nPlease restart JOSM with the -Xmx###M option,\n" + 48 "where ### is the the number of MB assigned to JOSM (e.g. 256).\n" + 49 "Currently, " + Runtime.getRuntime().maxMemory()/1024/1024 + " MB are available to JOSM."); 50 return; 51 } 52 52 53 53 PluginProxy plugin = null; 54 54 55 56 57 55 // Check for an explicit problem when calling a plugin function 56 if (e instanceof PluginException) 57 plugin = ((PluginException)e).plugin; 58 58 59 60 59 if (plugin == null) 60 plugin = guessPlugin(e); 61 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 62 if (plugin != null) { 63 int answer = JOptionPane.showConfirmDialog( 64 Main.parent, tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.", 65 plugin.info.name) + "\n"+ (plugin.info.author != null ? 66 tr("According to the information within the plugin, the author is {0}.", 67 plugin.info.author) : "") + "\n" + 68 tr("Try updating to the newest version of this plugin before reporting a bug.") + "\n" + 69 tr("Should the plugin be disabled?"), 70 tr("Disable plugin"), 71 JOptionPane.YES_NO_OPTION); 72 if (answer == JOptionPane.OK_OPTION) { 73 LinkedList<String> plugins = new LinkedList<String>(Arrays.asList(Main.pref.get("plugins").split(","))); 74 if (plugins.contains(plugin.info.name)) { 75 while (plugins.remove(plugin.info.name)) {} 76 String p = ""; 77 for (String s : plugins) 78 p += ","+s; 79 if (p.length() > 0) 80 p = p.substring(1); 81 Main.pref.put("plugins", p); 82 JOptionPane.showMessageDialog(Main.parent, 83 tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin.")); 84 } else { 85 JOptionPane.showMessageDialog(Main.parent, 86 tr("The plugin could not be removed. Please tell the people you got JOSM from about the problem.")); 87 } 88 return; 89 } 90 } 91 91 92 93 94 95 96 97 98 99 100 101 92 Object[] options = new String[]{tr("Do nothing"), tr("Report Bug")}; 93 int answer = JOptionPane.showOptionDialog(Main.parent, tr("An unexpected exception occurred.\n\n" + 94 "This is always a coding error. If you are running the latest\n" + 95 "version of JOSM, please consider being kind and file a bug report."), 96 tr("Unexpected Exception"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, 97 null, options, options[0]); 98 if (answer == 1) { 99 try { 100 StringWriter stack = new StringWriter(); 101 e.printStackTrace(new PrintWriter(stack)); 102 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 103 URL revUrl = Main.class.getResource("/REVISION"); 104 StringBuilder sb = new StringBuilder(); 105 if (revUrl == null) { 106 sb.append(tr("Development version. Unknown revision.")); 107 File f = new File("org/openstreetmap/josm/Main.class"); 108 if (!f.exists()) 109 f = new File("bin/org/openstreetmap/josm/Main.class"); 110 if (!f.exists()) 111 f = new File("build/org/openstreetmap/josm/Main.class"); 112 if (f.exists()) { 113 DateFormat sdf = SimpleDateFormat.getDateTimeInstance(); 114 sb.append("\nMain.class build on "+sdf.format(new Date(f.lastModified()))); 115 sb.append("\n"); 116 } 117 } else { 118 BufferedReader in = new BufferedReader(new InputStreamReader(revUrl.openStream())); 119 for (String line = in.readLine(); line != null; line = in.readLine()) { 120 sb.append(line); 121 sb.append('\n'); 122 } 123 } 124 sb.append("\n"+stack.getBuffer().toString()); 125 125 126 127 128 129 130 131 132 133 134 135 136 126 JPanel p = new JPanel(new GridBagLayout()); 127 p.add(new JLabel(tr("<html>Please report a ticket at {0}<br>" + 128 "Include your steps to get to the error (as detailed as possible)!<br>" + 129 "Be sure to include the following information:</html>", "http://josm.openstreetmap.de/newticket")), GBC.eol()); 130 try { 131 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(sb.toString()), new ClipboardOwner(){ 132 public void lostOwnership(Clipboard clipboard, Transferable contents) {} 133 }); 134 p.add(new JLabel(tr("(The text has already been copied to your clipboard.)")), GBC.eop()); 135 } 136 catch (RuntimeException x) {} 137 137 138 139 140 141 138 JTextArea info = new JTextArea(sb.toString(), 20, 60); 139 info.setCaretPosition(0); 140 info.setEditable(false); 141 p.add(new JScrollPane(info), GBC.eop()); 142 142 143 144 145 146 147 148 149 143 JOptionPane.showMessageDialog(Main.parent, p); 144 } catch (Exception e1) { 145 e1.printStackTrace(); 146 } 147 } 148 } 149 } 150 150 151 152 153 154 155 156 157 151 private PluginProxy guessPlugin(Throwable e) { 152 String name = guessPluginName(e); 153 for (PluginProxy p : Main.plugins) 154 if (p.info.name.equals(name)) 155 return p; 156 return null; 157 } 158 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 159 /** 160 * Analyze the stack of the argument and return a name of a plugin, if 161 * some known problem pattern has been found or <code>null</code>, if 162 * the stack does not contain plugin-code. 163 * 164 * Note: This heuristic is not meant as discrimination against specific 165 * plugins, but only to stop the flood of similar bug reports about plugins. 166 * Of course, plugin writers are free to install their own version of 167 * an exception handler with their email address listed to receive 168 * bug reports ;-). 169 */ 170 private String guessPluginName(Throwable e) { 171 for (StackTraceElement element : e.getStackTrace()) { 172 String c = element.getClassName(); 173 173 174 175 176 177 178 179 180 181 182 183 184 185 174 if (c.contains("wmsplugin.") || c.contains(".WMSLayer")) 175 return "wmsplugin"; 176 if (c.contains("landsat.") || c.contains(".LandsatLayer")) 177 return "landsat"; 178 if (c.contains("livegps.")) 179 return "livegps"; 180 if (c.contains("mappaint.")) 181 return "mappaint"; 182 if (c.contains("annotationtester.")) 183 return "annotation-tester"; 184 if (c.startsWith("UtilsPlugin.")) 185 return "UtilsPlugin"; 186 186 187 188 189 190 191 192 193 194 195 187 if (c.startsWith("org.openstreetmap.josm.plugins.")) { 188 String p = c.substring("org.openstreetmap.josm.plugins.".length()); 189 if (p.indexOf('.') != -1 && p.matches("[a-z].*")) { 190 return p.substring(0,p.indexOf('.')); 191 } 192 } 193 } 194 return null; 195 } 196 196 } -
trunk/src/org/openstreetmap/josm/tools/ColorHelper.java
r885 r1169 8 8 */ 9 9 public class ColorHelper { 10 11 public static Color html2color(String html) {12 if (html.length() > 0 && html.charAt(0) == '#')13 html = html.substring(1);14 else if (html.length() != 6 && html.length() != 8)15 return null;16 try {17 return new Color(18 Integer.parseInt(html.substring(0,2),16),19 Integer.parseInt(html.substring(2,4),16),20 Integer.parseInt(html.substring(4,6),16),21 (html.length() == 8 ? Integer.parseInt(html.substring(6,8),16) : 255));22 } catch (NumberFormatException e) {23 return null;24 }25 }26 10 27 private static String int2hex(int i) { 28 String s = Integer.toHexString(i / 16) + Integer.toHexString(i % 16); 29 return s.toUpperCase(); 30 } 31 32 public static String color2html(Color col) { 33 return "#"+int2hex(col.getRed())+int2hex(col.getGreen())+int2hex(col.getBlue()); 34 } 11 public static Color html2color(String html) { 12 if (html.length() > 0 && html.charAt(0) == '#') 13 html = html.substring(1); 14 else if (html.length() != 6 && html.length() != 8) 15 return null; 16 try { 17 return new Color( 18 Integer.parseInt(html.substring(0,2),16), 19 Integer.parseInt(html.substring(2,4),16), 20 Integer.parseInt(html.substring(4,6),16), 21 (html.length() == 8 ? Integer.parseInt(html.substring(6,8),16) : 255)); 22 } catch (NumberFormatException e) { 23 return null; 24 } 25 } 26 27 private static String int2hex(int i) { 28 String s = Integer.toHexString(i / 16) + Integer.toHexString(i % 16); 29 return s.toUpperCase(); 30 } 31 32 public static String color2html(Color col) { 33 return "#"+int2hex(col.getRed())+int2hex(col.getGreen())+int2hex(col.getBlue()); 34 } 35 35 } -
trunk/src/org/openstreetmap/josm/tools/DateParser.java
r627 r1169 8 8 /** 9 9 * Tries to parse a date as good as it can. 10 * 10 * 11 11 * @author Immanuel.Scholz 12 12 */ 13 13 public class DateParser { 14 15 16 14 public static Date parse(String d) throws ParseException { 15 return new PrimaryDateParser().parse(d); 16 } 17 17 } -
trunk/src/org/openstreetmap/josm/tools/Destroyable.java
r627 r1169 6 6 * been removed) have an definite set of actions to execute. This is the "destructor" interface called 7 7 * on those objects. 8 * 8 * 9 9 * @author immanuel.scholz 10 10 */ 11 11 public interface Destroyable { 12 12 13 14 15 16 13 /** 14 * Called when the object has been destroyed. 15 */ 16 public void destroy(); 17 17 } -
trunk/src/org/openstreetmap/josm/tools/DontShowAgainInfo.java
r1004 r1169 16 16 public class DontShowAgainInfo { 17 17 18 19 20 18 public static boolean show(String prefKey, String msg) { 19 return show(prefKey, new JLabel(msg), true, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION); 20 } 21 21 22 23 24 22 public static boolean show(String prefKey, String msg, Boolean state) { 23 return show(prefKey, new JLabel(msg), state, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION); 24 } 25 25 26 27 28 26 public static boolean show(String prefKey, Container msg) { 27 return show(prefKey, msg, true, JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_OPTION); 28 } 29 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 30 public static boolean show(String prefKey, Container msg, Boolean state, int options, int true_option) { 31 if (!Main.pref.getBoolean("message."+prefKey)) { 32 JCheckBox dontshowagain = new JCheckBox(tr("Do not show again")); 33 dontshowagain.setSelected(Main.pref.getBoolean("message."+prefKey, state)); 34 JPanel all = new JPanel(new GridBagLayout()); 35 all.add(msg, GBC.eop()); 36 all.add(dontshowagain, GBC.eol()); 37 int answer = JOptionPane.showConfirmDialog(Main.parent, all, tr("Information"), options); 38 if (answer != true_option) 39 return false; 40 Main.pref.put("message."+prefKey, dontshowagain.isSelected()); 41 } 42 return true; 43 } 44 44 } -
trunk/src/org/openstreetmap/josm/tools/ExifReader.java
r627 r1169 18 18 public class ExifReader { 19 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 20 @SuppressWarnings("unchecked") public static Date readTime(File filename) throws ParseException { 21 Date date = null; 22 try { 23 Metadata metadata = JpegMetadataReader.readMetadata(filename); 24 for (Iterator<Directory> dirIt = metadata.getDirectoryIterator(); dirIt.hasNext();) { 25 for (Iterator<Tag> tagIt = dirIt.next().getTagIterator(); tagIt.hasNext();) { 26 Tag tag = tagIt.next(); 27 if (tag.getTagType() == 0x9003) 28 return DateParser.parse(tag.getDescription()); 29 if (tag.getTagType() == 0x132 || tag.getTagType() == 0x9004) 30 date = DateParser.parse(tag.getDescription()); 31 } 32 } 33 } catch (ParseException e) { 34 throw e; 35 35 } catch (Exception e) { 36 36 e.printStackTrace(); 37 37 } 38 39 38 return date; 39 } 40 40 } -
trunk/src/org/openstreetmap/josm/tools/FallbackDateParser.java
r627 r1169 13 13 * based on similar code in JOSM. This class is not threadsafe, a separate 14 14 * instance must be created per thread. 15 * 15 * 16 16 * @author Brett Henderson 17 17 */ 18 18 public class FallbackDateParser { 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 * 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 19 20 private static final String[] formats = { 21 "yyyy-MM-dd'T'HH:mm:ss'Z'", 22 "yyyy-MM-dd'T'HH:mm:ssZ", 23 "yyyy-MM-dd'T'HH:mm:ss", 24 "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", 25 "yyyy-MM-dd'T'HH:mm:ss.SSSZ", 26 "yyyy-MM-dd HH:mm:ss", 27 "MM/dd/yyyy HH:mm:ss", 28 "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'", 29 "MM/dd/yyyy'T'HH:mm:ss.SSSZ", 30 "MM/dd/yyyy'T'HH:mm:ss.SSS", 31 "MM/dd/yyyy'T'HH:mm:ssZ", 32 "MM/dd/yyyy'T'HH:mm:ss", 33 "yyyy:MM:dd HH:mm:ss" 34 }; 35 36 37 private List<DateFormat> dateParsers; 38 private int activeDateParser; 39 40 41 /** 42 * Creates a new instance. 43 */ 44 public FallbackDateParser() { 45 // Build a list of candidate date parsers. 46 dateParsers = new ArrayList<DateFormat>(formats.length); 47 for (int i = 0; i < formats.length; i++) { 48 dateParsers.add(new SimpleDateFormat(formats[i])); 49 } 50 51 // We haven't selected a date parser yet. 52 activeDateParser = -1; 53 } 54 55 56 /** 57 * Attempts to parse the specified date. 58 * 59 * @param date 60 * The date to parse. 61 * @return The date. 62 * @throws ParseException 63 * Occurs if the date does not match any of the supported date 64 * formats. 65 */ 66 public Date parse(String date) throws ParseException { 67 String correctedDate; 68 69 // Try to fix ruby's broken xmlschema - format 70 // Replace this: 71 // 2007-02-12T18:43:01+00:00 72 // With this: 73 // 2007-02-12T18:43:01+0000 74 if (date.length() == 25 && date.charAt(22) == ':') { 75 correctedDate = date.substring(0, 22) + date.substring(23, 25); 76 } else { 77 correctedDate = date; 78 } 79 80 // If we have previously successfully used a date parser, we'll try it 81 // first. 82 if (activeDateParser >= 0) { 83 try { 84 return dateParsers.get(activeDateParser).parse(correctedDate); 85 } catch (ParseException e) { 86 // The currently active parser didn't work, so we must clear it 87 // and find a new appropriate parser. 88 activeDateParser = -1; 89 } 90 } 91 92 // Try the date parsers one by one until a suitable format is found. 93 for (int i = 0; i < dateParsers.size(); i++) { 94 try { 95 Date result; 96 97 // Attempt to parse with the current parser, if successful we 98 // store its index for next time. 99 result = dateParsers.get(i).parse(correctedDate); 100 activeDateParser = i; 101 102 return result; 103 104 } catch (ParseException pe) { 105 // Ignore parsing errors and try the next pattern. 106 } 107 } 108 109 throw new ParseException("The date string (" + date + ") could not be parsed.", 0); 110 } 111 111 } -
trunk/src/org/openstreetmap/josm/tools/GBC.java
r627 r1169 12 12 * A wrapper for GridBagConstraints which has sane default static creators and 13 13 * member functions to chain calling. 14 * 14 * 15 15 * @author imi 16 16 */ 17 17 public class GBC extends GridBagConstraints { 18 18 19 20 21 22 19 /** 20 * Use public static creator functions to create an GBC. 21 */ 22 private GBC() {} 23 23 24 25 26 27 28 29 30 31 32 24 /** 25 * Create a standard constraint (which is not the last). 26 * @return A standard constraint with no filling. 27 */ 28 public static GBC std() { 29 GBC c = new GBC(); 30 c.anchor = WEST; 31 return c; 32 } 33 33 34 35 36 37 38 39 40 41 42 34 /** 35 * Create the constraint for the last elements on a line. 36 * @return A constraint which indicates the last item on a line. 37 */ 38 public static GBC eol() { 39 GBC c = std(); 40 c.gridwidth = REMAINDER; 41 return c; 42 } 43 43 44 45 46 47 48 49 50 51 44 /** 45 * Create the constraint for the last elements on a line and on a paragraph. 46 * This is merely a shortcut for eol().insets(0,0,0,10) 47 * @return A constraint which indicates the last item on a line. 48 */ 49 public static GBC eop() { 50 return eol().insets(0,0,0,10); 51 } 52 52 53 54 55 56 57 58 59 53 /** 54 * Try to fill both, horizontal and vertical 55 * @return This constraint for chaining. 56 */ 57 public GBC fill() { 58 return fill(BOTH); 59 } 60 60 61 62 63 64 65 66 67 68 69 70 71 72 73 61 /** 62 * Set fill to the given value 63 * @param value The filling value, either NONE, HORIZONTAL, VERTICAL or BOTH 64 * @return This constraint for chaining. 65 */ 66 public GBC fill(int value) { 67 fill = value; 68 if (value == HORIZONTAL || value == BOTH) 69 weightx = 1.0; 70 if (value == VERTICAL || value == BOTH) 71 weighty = 1.0; 72 return this; 73 } 74 74 75 76 77 78 79 80 81 82 83 75 /** 76 * Set the anchor of this GBC to a. 77 * @param a The new anchor, e.g. GBC.CENTER or GBC.EAST. 78 * @return This constraint for chaining. 79 */ 80 public GBC anchor(int a) { 81 anchor = a; 82 return this; 83 } 84 84 85 86 87 * @param leftThe left space of the insets88 * @param topThe top space of the insets89 * @param rightThe right space of the insets90 * @param bottomThe bottom space of the insets91 92 93 94 95 96 85 /** 86 * Adds insets to this GBC. 87 * @param left The left space of the insets 88 * @param top The top space of the insets 89 * @param right The right space of the insets 90 * @param bottom The bottom space of the insets 91 * @return This constraint for chaining. 92 */ 93 public GBC insets(int left, int top, int right, int bottom) { 94 insets = new Insets(top, left, bottom, right); 95 return this; 96 } 97 97 98 99 100 101 *horizontal strut.102 103 *vertical strut.104 105 106 107 108 109 98 /** 99 * This is a helper to easily create a glue with a minimum default value. 100 * @param x If higher than 0, this will be a horizontal glue with x as minimum 101 * horizontal strut. 102 * @param y If higher than 0, this will be a vertical glue with y as minimum 103 * vertical strut. 104 */ 105 public static Component glue(int x, int y) { 106 short maxx = x > 0 ? Short.MAX_VALUE : 0; 107 short maxy = y > 0 ? Short.MAX_VALUE : 0; 108 return new Box.Filler(new Dimension(x,y), new Dimension(x,y), new Dimension(maxx,maxy)); 109 } 110 110 } -
trunk/src/org/openstreetmap/josm/tools/I18n.java
r1065 r1169 10 10 /** 11 11 * Internationalisation support. 12 * 12 * 13 13 * @author Immanuel.Scholz 14 14 */ 15 15 public class I18n { 16 16 17 18 17 /* Base name for translation data. Used for detecting available translations */ 18 private static final String TR_BASE = "org.openstreetmap.josm.i18n.Translation_"; 19 19 20 21 22 23 24 20 /** 21 * Set by MainApplication. Changes here later will probably mess up everything, because 22 * many strings are already loaded. 23 */ 24 public static org.xnap.commons.i18n.I18n i18n; 25 25 26 27 28 29 30 26 public static final String tr(String text, Object... objects) { 27 if (i18n == null) 28 return MessageFormat.format(text, objects); 29 return i18n.tr(text, objects); 30 } 31 31 32 33 34 35 36 32 public static final String tr(String text) { 33 if (i18n == null) 34 return text; 35 return i18n.tr(text); 36 } 37 37 38 39 40 38 public static final String marktr(String text) { 39 return text; 40 } 41 41 42 43 44 45 46 42 public static final String trn(String text, String pluralText, long n, Object... objects) { 43 if (i18n == null) 44 return n == 1 ? tr(text, objects) : tr(pluralText, objects); 45 return i18n.trn(text, pluralText, n, objects); 46 } 47 47 48 49 50 51 52 48 public static final String trn(String text, String pluralText, long n) { 49 if (i18n == null) 50 return n == 1 ? tr(text) : tr(pluralText); 51 return i18n.trn(text, pluralText, n); 52 } 53 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 54 /** 55 * Get a list of all available JOSM Translations. 56 * @return an array of locale objects. 57 */ 58 public static final Locale[] getAvailableTranslations() { 59 Vector<Locale> v = new Vector<Locale>(); 60 Locale[] l = Locale.getAvailableLocales(); 61 for (int i = 0; i < l.length; i++) { 62 String cn = TR_BASE + l[i]; 63 try { 64 Class.forName(cn); 65 v.add(l[i]); 66 } catch (ClassNotFoundException e) { 67 } 68 } 69 l = new Locale[v.size()]; 70 l = v.toArray(l); 71 Arrays.sort(l, new Comparator<Locale>() { 72 public int compare(Locale o1, Locale o2) { 73 return o1.toString().compareTo(o2.toString()); 74 } 75 }); 76 return l; 77 } 78 78 } -
trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
r991 r1169 32 32 public class ImageProvider { 33 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 * @param subdirThe position of the directory, e.g. "layer"55 * @param nameThe icons name (without the ending of ".png")56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 34 /** 35 * Position of an overlay icon 36 * @author imi 37 */ 38 public static enum OverlayPosition {NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST} 39 40 /** 41 * The icon cache 42 */ 43 private static Map<String, Image> cache = new HashMap<String, Image>(); 44 45 /** 46 * Add here all ClassLoader whose ressource should be searched. 47 * Plugin's class loaders are added by main. 48 */ 49 public static final List<ClassLoader> sources = new LinkedList<ClassLoader>(); 50 51 /** 52 * Return an image from the specified location. 53 * 54 * @param subdir The position of the directory, e.g. "layer" 55 * @param name The icons name (without the ending of ".png") 56 * @return The requested Image. 57 */ 58 public static ImageIcon get(String subdir, String name) { 59 ImageIcon icon = getIfAvailable(subdir, name); 60 if (icon == null) { 61 String ext = name.indexOf('.') != -1 ? "" : ".png"; 62 throw new NullPointerException("/images/"+subdir+name+ext+" not found"); 63 } 64 return icon; 65 } 66 67 public static ImageIcon getIfAvailable(String subdir, String name) 68 { 69 return getIfAvailable((Collection<String>)null, null, subdir, name); 70 } 71 public static final ImageIcon getIfAvailable(String[] dirs, String id, String subdir, String name) 72 { 73 return getIfAvailable(Arrays.asList(dirs), id, subdir, name); 74 } 75 76 /** 77 * Like {@link #get(String)}, but does not throw and return <code>null</code> 78 * in case of nothing is found. Use this, if the image to retrieve is optional. 79 */ 80 public static ImageIcon getIfAvailable(Collection<String> dirs, String id, String subdir, String name) 81 { 82 if (name == null) 83 return null; 84 if (subdir == null) 85 subdir = ""; 86 else if (!subdir.equals("")) 87 subdir += "/"; 88 String ext = name.indexOf('.') != -1 ? "" : ".png"; 89 String full_name = subdir+name+ext; 90 String cache_name = full_name; 91 /* cache separately */ 92 if(dirs != null && dirs.size() > 0) 93 cache_name = "id:"+id+":"+full_name; 94 95 Image img = cache.get(cache_name); 96 if (img == null) { 97 // getImageUrl() does a ton of "stat()" calls and gets expensive 98 // and redundant when you have a whole ton of objects. So, 99 // index the cache by the name of the icon we're looking for 100 // and don't bother to create a URL unless we're actually 101 // creating the image. 102 URL path = getImageUrl(full_name, dirs); 103 if (path == null) 104 return null; 105 img = Toolkit.getDefaultToolkit().createImage(path); 106 cache.put(cache_name, img); 107 } 108 109 return new ImageIcon(img); 110 } 111 112 private static URL getImageUrl(String path, String name) 113 { 114 if(path.startsWith("resource://")) 115 { 116 String p = path.substring("resource://".length()); 117 for (ClassLoader source : sources) 118 { 119 URL res; 120 if ((res = source.getResource(p+name)) != null) 121 return res; 122 } 123 } 124 else 125 { 126 try { 127 File f = new File(path, name); 128 if(f.exists()) 129 return f.toURI().toURL(); 130 } catch (MalformedURLException e) {} 131 } 132 return null; 133 } 134 135 private static URL getImageUrl(String imageName, Collection<String> dirs) 136 { 137 URL u; 138 // Try passed directories first 139 if(dirs != null) 140 { 141 for (String name : dirs) 142 { 143 u = getImageUrl(name, imageName); 144 if(u != null) return u; 145 } 146 } 147 // Try user-preference directory 148 u = getImageUrl(Main.pref.getPreferencesDir()+"images", imageName); 149 if(u != null) return u; 150 151 // Try plugins and josm classloader 152 u = getImageUrl("resource://images/", imageName); 153 if(u != null) return u; 154 155 // Try all other ressource directories 156 for (String location : Main.pref.getAllPossiblePreferenceDirs()) 157 { 158 u = getImageUrl(location+"images", imageName); 159 if(u != null) return u; 160 u = getImageUrl(location, imageName); 161 if(u != null) return u; 162 } 163 return null; 164 } 165 166 /** 167 * Shortcut for get("", name); 168 */ 169 public static ImageIcon get(String name) { 170 return get("", name); 171 } 172 173 public static Cursor getCursor(String name, String overlay) { 174 ImageIcon img = get("cursor",name); 175 if (overlay != null) 176 img = overlay(img, "cursor/modifier/"+overlay, OverlayPosition.SOUTHEAST); 177 Cursor c = Toolkit.getDefaultToolkit().createCustomCursor(img.getImage(), 178 name.equals("crosshair") ? new Point(10,10) : new Point(3,2), "Cursor"); 179 return c; 180 } 181 182 /** 183 * @return an icon that represent the overlay of the two given icons. The 184 * second icon is layed on the first relative to the given position. 185 */ 186 public static ImageIcon overlay(Icon ground, String overlayImage, OverlayPosition pos) { 187 GraphicsConfiguration conf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); 188 int w = ground.getIconWidth(); 189 int h = ground.getIconHeight(); 190 ImageIcon overlay = ImageProvider.get(overlayImage); 191 int wo = overlay.getIconWidth(); 192 int ho = overlay.getIconHeight(); 193 BufferedImage img = conf.createCompatibleImage(w,h, Transparency.TRANSLUCENT); 194 Graphics g = img.createGraphics(); 195 ground.paintIcon(null, g, 0, 0); 196 int x = 0, y = 0; 197 switch (pos) { 198 case NORTHWEST: 199 x = 0; 200 y = 0; 201 break; 202 case NORTHEAST: 203 x = w-wo; 204 y = 0; 205 break; 206 case SOUTHWEST: 207 x = 0; 208 y = h-ho; 209 break; 210 case SOUTHEAST: 211 x = w-wo; 212 y = h-ho; 213 break; 214 } 215 overlay.paintIcon(null, g, x, y); 216 return new ImageIcon(img); 217 } 218 219 static { 220 try { 221 sources.add(ClassLoader.getSystemClassLoader()); 222 } catch (SecurityException ex) { 223 sources.add(ImageProvider.class.getClassLoader()); 224 } 225 } 226 226 } -
trunk/src/org/openstreetmap/josm/tools/OpenBrowser.java
r1023 r1169 19 19 public class OpenBrowser { 20 20 21 22 23 24 25 26 27 28 29 30 31 32 33 21 /** 22 * @return <code>null</code> for success or a string in case of an error. 23 */ 24 public static String displayUrl(String url) { 25 if (Main.applet) { 26 try { 27 JApplet applet = (JApplet) Main.parent; 28 applet.getAppletContext().showDocument(new URL(url)); 29 return null; 30 } catch (MalformedURLException mue) { 31 return mue.getMessage(); 32 } 33 } 34 34 35 36 37 38 39 40 41 35 try { 36 Main.platform.openUrl(url); 37 } catch (IOException e) { 38 return e.getMessage(); 39 } 40 return null; 41 } 42 42 43 43 } -
trunk/src/org/openstreetmap/josm/tools/Pair.java
r627 r1169 6 6 */ 7 7 public final class Pair<A,B> { 8 9 8 public A a; 9 public B b; 10 10 11 12 13 14 11 public Pair(A a, B b) { 12 this.a = a; 13 this.b = b; 14 } 15 15 16 17 18 16 @Override public int hashCode() { 17 return a.hashCode() ^ b.hashCode(); 18 } 19 19 20 21 22 23 20 @Override public boolean equals(Object o) { 21 return o == null ? o == null : o instanceof Pair 22 && a.equals(((Pair<?,?>) o).a) && b.equals(((Pair<?,?>) o).b); 23 } 24 24 25 26 27 28 29 30 25 public static <T> ArrayList<T> toArrayList(Pair<T, T> p) { 26 ArrayList<T> l = new ArrayList<T>(2); 27 l.add(p.a); 28 l.add(p.b); 29 return l; 30 } 31 31 32 33 34 35 36 37 38 39 32 public static <T> Pair<T,T> sort(Pair<T,T> p) { 33 if (p.b.hashCode() < p.a.hashCode()) { 34 T tmp = p.a; 35 p.a = p.b; 36 p.b = tmp; 37 } 38 return p; 39 } 40 40 } -
trunk/src/org/openstreetmap/josm/tools/PlatformHook.java
r1084 r1169 29 29 */ 30 30 public interface PlatformHook { 31 32 33 34 35 36 37 38 39 31 /** 32 * The preStartupHook will be called extremly early. It is 33 * guaranteed to be called before the GUI setup has started. 34 * 35 * Reason: On OSX we need to inform the Swing libraries 36 * that we want to be integrated with the OS before we setup 37 * our GUI. 38 */ 39 public void preStartupHook(); 40 40 41 42 43 44 45 46 47 48 41 /** 42 * The startupHook will be called early, but after the GUI 43 * setup has started. 44 * 45 * Reason: On OSX we need to register some callbacks with the 46 * OS, so we'll receive events from the system menu. 47 */ 48 public void startupHook(); 49 49 50 51 52 53 54 50 /** 51 * The openURL hook will be used to open an URL in the 52 * default webbrowser. 53 */ 54 public void openUrl(String url) throws IOException; 55 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 56 /** 57 * The initShortcutGroups hook will be called by the 58 * Shortcut class if it detects that there are no 59 * groups in teh config file. So that will happen 60 * once on each JOSM installation only. 61 * 62 * Please note that ShorCut will load its config on demand, 63 * that is, at the moment the first shortcut is registered. 64 * 65 * In this hook, you have to fill the preferences with 66 * data, not the internal structures! Also, do not try 67 * to register any shortcuts from within. 68 */ 69 public void initShortcutGroups(); 70 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 71 /** 72 * The initSystemShortcuts hook will be called by the 73 * Shortcut class after the modifier groups have been read 74 * from the config, but before any shortcuts are read from 75 * it or registered from within the application. 76 * 77 * Plese note that you are not allowed to register any 78 * shortuts from this hook, but only "systemCuts"! 79 * 80 * BTW: SystemCuts should be named "system:<whatever>", 81 * and it'd be best if sou'd recycle the names already used 82 * by the Windows and OSX hooks. Especially the later has 83 * really many of them. 84 * 85 * You should also register any and all shortcuts that the 86 * operation system handles itself to block JOSM from trying 87 * to use them---as that would just not work. Call setAutomatic 88 * on them to prevent the keyboard preferences from allowing the 89 * user to change them. 90 */ 91 public void initSystemShortcuts(); 92 92 93 94 95 96 97 98 99 100 101 102 103 104 93 /** 94 * The makeTooltip hook will be called whenever a tooltip for 95 * a menu or button is created. 96 * 97 * Tooltips are usually not system dependent, unless the 98 * JVM is to dumb to provide correct names for all the keys. 99 * 100 * Another reason not to use the implementation in the *nix 101 * hook are LAFs that don't understand HTML, such as the OSX 102 * LAFs. 103 */ 104 public String makeTooltip(String name, Shortcut sc); 105 105 } -
trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java
r1084 r1169 16 16 */ 17 17 public class PlatformHookOsx extends PlatformHookUnixoid implements PlatformHook, InvocationHandler { 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 18 private static PlatformHookOsx ivhandler = new PlatformHookOsx(); 19 public void preStartupHook(){ 20 // This will merge our MenuBar into the system menu. 21 // MUST be set before Swing is initialized! 22 // And will not work when one of the system independet LAFs is used. 23 // They just insist on painting themselves... 24 System.setProperty("apple.laf.useScreenMenuBar", "true"); 25 } 26 public void startupHook() { 27 // Here we register callbacks for the menu entries in the system menu 28 try { 29 Class Ccom_apple_eawt_Application = Class.forName("com.apple.eawt.Application"); 30 Object Ocom_apple_eawt_Application = Ccom_apple_eawt_Application.getConstructor((Class[])null).newInstance((Object[])null); 31 Class Ccom_apple_eawt_ApplicationListener = Class.forName("com.apple.eawt.ApplicationListener"); 32 Method MaddApplicationListener = Ccom_apple_eawt_Application.getDeclaredMethod("addApplicationListener", new Class[] { Ccom_apple_eawt_ApplicationListener }); 33 Object Oproxy = Proxy.newProxyInstance(PlatformHookOsx.class.getClassLoader(), new Class[] { Ccom_apple_eawt_ApplicationListener }, ivhandler); 34 MaddApplicationListener.invoke(Ocom_apple_eawt_Application, new Object[] { Oproxy }); 35 Method MsetEnabledPreferencesMenu = Ccom_apple_eawt_Application.getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class }); 36 MsetEnabledPreferencesMenu.invoke(Ocom_apple_eawt_Application, new Object[] { Boolean.TRUE }); 37 } catch (Exception ex) { 38 // Oops, what now? 39 // We'll just ignore this for now. The user will still be able to close JOSM 40 // by closing all its windows. 41 System.out.println("Failed to register with OSX: " + ex); 42 } 43 } 44 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { 45 Boolean handled = Boolean.TRUE; 46 //System.out.println("Going to handle method "+method+" (short: "+method.getName()+") with event "+args[0]); 47 if (method.getName().equals("handleQuit")) { 48 handled = !Main.main.breakBecauseUnsavedChanges(); 49 } else if (method.getName().equals("handleAbout")) { 50 Main.main.menu.about.actionPerformed(null); 51 } else if (method.getName().equals("handlePreferences")) { 52 Main.main.menu.preferences.actionPerformed(null); 53 } else { 54 return null; 55 } 56 if (args[0] != null) { 57 try { 58 args[0].getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }).invoke(args[0], new Object[] { handled }); 59 } catch (Exception ex) { 60 System.out.println("Failed to report handled event: " + ex); 61 } 62 } 63 return null; 64 } 65 public void openUrl(String url) throws IOException { 66 // Ain't that KISS? 67 Runtime.getRuntime().exec("open " + url); 68 } 69 public void initShortcutGroups() { 70 // Everything but Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU is guesswork. 71 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE), Integer.toString(-1)); 72 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 73 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.META_DOWN_MASK)); 74 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT), Integer.toString(0)); 75 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 76 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT), Integer.toString(0)); 77 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK)); 78 79 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE), Integer.toString(-1)); 80 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 81 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 82 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK)); 83 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 84 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK)); 85 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 86 87 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE), Integer.toString(-1)); 88 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 89 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 90 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT), Integer.toString(KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 91 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 92 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 93 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 94 } 95 public void initSystemShortcuts() { 96 // Yeah, it's a long, long list. And people always complain that OSX has no shortcuts. 97 Shortcut.registerSystemShortcut("apple-reserved-01", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Show or hide the Spotlight search field (when multiple languages are installed, may rotate through enabled script systems). 98 Shortcut.registerSystemShortcut("apple-reserved-02", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Apple reserved. 99 Shortcut.registerSystemShortcut("apple-reserved-03", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Show the Spotlight search results window (when multiple languages are installed, may rotate through keyboard layouts and input methods within a script). 100 Shortcut.registerSystemShortcut("apple-reserved-04", "reserved", KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // | Apple reserved. 101 Shortcut.registerSystemShortcut("apple-reserved-05", "reserved", KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Navigate through controls in a reverse direction. See "Keyboard Focus and Navigation." 102 Shortcut.registerSystemShortcut("apple-reserved-06", "reserved", KeyEvent.VK_TAB, KeyEvent.META_DOWN_MASK).setAutomatic(); // Move forward to the next most recently used application in a list of open applications. 103 Shortcut.registerSystemShortcut("apple-reserved-07", "reserved", KeyEvent.VK_TAB, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move backward through a list of open applications (sorted by recent use). 104 Shortcut.registerSystemShortcut("apple-reserved-08", "reserved", KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the next grouping of controls in a dialog or the next table (when Tab moves to the next cell). See Accessibility Overview. 105 Shortcut.registerSystemShortcut("apple-reserved-09", "reserved", KeyEvent.VK_TAB, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previous grouping of controls. See Accessibility Overview. 106 Shortcut.registerSystemShortcut("apple-reserved-10", "reserved", KeyEvent.VK_ESCAPE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Open Front Row. 107 Shortcut.registerSystemShortcut("apple-reserved-11", "reserved", KeyEvent.VK_ESCAPE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Open the Force Quit dialog. 108 Shortcut.registerSystemShortcut("apple-reserved-12", "reserved", KeyEvent.VK_F1, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Toggle full keyboard access on or off. See Accessibility Overview. 109 Shortcut.registerSystemShortcut("apple-reserved-13", "reserved", KeyEvent.VK_F2, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the menu bar. See Accessibility Overview. 110 Shortcut.registerSystemShortcut("apple-reserved-14", "reserved", KeyEvent.VK_F3, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the Dock. See Accessibility Overview. 111 Shortcut.registerSystemShortcut("apple-reserved-15", "reserved", KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the active (or next) window. See Accessibility Overview. 112 Shortcut.registerSystemShortcut("apple-reserved-16", "reserved", KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previously active window. See Accessibility Overview. 113 Shortcut.registerSystemShortcut("apple-reserved-17", "reserved", KeyEvent.VK_F5, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the toolbar. See Accessibility Overview. 114 Shortcut.registerSystemShortcut("apple-reserved-18", "reserved", KeyEvent.VK_F5, KeyEvent.META_DOWN_MASK).setAutomatic(); // Turn VoiceOver on or off. See Accessibility Overview. 115 Shortcut.registerSystemShortcut("apple-reserved-19", "reserved", KeyEvent.VK_F6, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Move focus to the first (or next) panel. See Accessibility Overview. 116 Shortcut.registerSystemShortcut("apple-reserved-20", "reserved", KeyEvent.VK_F6, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Move focus to the previous panel. See Accessibility Overview. 117 Shortcut.registerSystemShortcut("apple-reserved-21", "reserved", KeyEvent.VK_F7, KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Temporarily override the current keyboard access mode in windows and dialogs. See Accessibility Overview. 118 Shortcut.registerSystemShortcut("apple-reserved-22", "reserved", KeyEvent.VK_F9, 0).setAutomatic(); // Tile or untile all open windows. 119 Shortcut.registerSystemShortcut("apple-reserved-23", "reserved", KeyEvent.VK_F10, 0).setAutomatic(); // Tile or untile all open windows in the currently active application. 120 Shortcut.registerSystemShortcut("apple-reserved-24", "reserved", KeyEvent.VK_F11, 0).setAutomatic(); // Hide or show all open windows. 121 Shortcut.registerSystemShortcut("apple-reserved-25", "reserved", KeyEvent.VK_F12, 0).setAutomatic(); // Hide or display Dashboard. 122 Shortcut.registerSystemShortcut("apple-reserved-26", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK).setAutomatic(); // Activate the next open window in the frontmost application. See "Window Layering." 123 Shortcut.registerSystemShortcut("apple-reserved-27", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Activate the previous open window in the frontmost application. See "Window Layering." 124 Shortcut.registerSystemShortcut("apple-reserved-28", "reserved", KeyEvent.VK_DEAD_GRAVE, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Move focus to the window drawer. 125 Shortcut.registerSystemShortcut("apple-reserved-29", "reserved", KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK).setAutomatic(); // Decrease the size of the selected item (equivalent to the Smaller command). See "The Format Menu." 126 Shortcut.registerSystemShortcut("apple-reserved-30", "reserved", KeyEvent.VK_MINUS, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Zoom out when screen zooming is on. See Accessibility Overview. 127 128 Shortcut.registerSystemShortcut("system:align-left", "reserved", KeyEvent.VK_OPEN_BRACKET, KeyEvent.META_DOWN_MASK); // Left-align a selection (equivalent to the Align Left command). See "The Format Menu." 129 Shortcut.registerSystemShortcut("system:align-right","reserved", KeyEvent.VK_CLOSE_BRACKET, KeyEvent.META_DOWN_MASK); // Right-align a selection (equivalent to the Align Right command). See "The Format Menu." 130 // I found no KeyEvent for | 131 //Shortcut.registerSystemCut("system:align-center", "reserved", '|', KeyEvent.META_DOWN_MASK); // Center-align a selection (equivalent to the Align Center command). See "The Format Menu." 132 Shortcut.registerSystemShortcut("system:spelling", "reserved", KeyEvent.VK_COLON, KeyEvent.META_DOWN_MASK); // Display the Spelling window (equivalent to the Spelling command). See "The Edit Menu." 133 Shortcut.registerSystemShortcut("system:spellcheck", "reserved", KeyEvent.VK_SEMICOLON, KeyEvent.META_DOWN_MASK); // Find misspelled words in the document (equivalent to the Check Spelling command). See "The Edit Menu." 134 Shortcut.registerSystemShortcut("system:preferences", "reserved", KeyEvent.VK_COMMA, KeyEvent.META_DOWN_MASK).setAutomatic(); // Open the application's preferences window (equivalent to the Preferences command). See "The Application Menu." 135 136 Shortcut.registerSystemShortcut("apple-reserved-31", "reserved", KeyEvent.VK_COMMA, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Decrease screen contrast. See Accessibility Overview. 137 Shortcut.registerSystemShortcut("apple-reserved-32", "reserved", KeyEvent.VK_PERIOD, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Increase screen contrast. See Accessibility Overview. 138 139 // I found no KeyEvent for ? 140 //Shortcut.registerSystemCut("system:help", "reserved", '?', KeyEvent.META_DOWN_MASK).setAutomatic(); // Open the application's help in Help Viewer. See "The Help Menu." 141 142 Shortcut.registerSystemShortcut("apple-reserved-33", "reserved", KeyEvent.VK_SLASH, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Turn font smoothing on or off. 143 Shortcut.registerSystemShortcut("apple-reserved-34", "reserved", KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Increase the size of the selected item (equivalent to the Bigger command). See "The Format Menu." 144 Shortcut.registerSystemShortcut("apple-reserved-35", "reserved", KeyEvent.VK_EQUALS, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Zoom in when screen zooming is on. See Accessibility Overview. 145 Shortcut.registerSystemShortcut("apple-reserved-36", "reserved", KeyEvent.VK_3, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Capture the screen to a file. 146 Shortcut.registerSystemShortcut("apple-reserved-37", "reserved", KeyEvent.VK_3, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Capture the screen to the Clipboard. 147 Shortcut.registerSystemShortcut("apple-reserved-38", "reserved", KeyEvent.VK_4, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Capture a selection to a file. 148 Shortcut.registerSystemShortcut("apple-reserved-39", "reserved", KeyEvent.VK_4, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Capture a selection to the Clipboard. 149 Shortcut.registerSystemShortcut("apple-reserved-40", "reserved", KeyEvent.VK_8, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Turn screen zooming on or off. See Accessibility Overview. 150 Shortcut.registerSystemShortcut("apple-reserved-41", "reserved", KeyEvent.VK_8, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Invert the screen colors. See Accessibility Overview. 151 152 Shortcut.registerSystemShortcut("system:selectall", "reserved", KeyEvent.VK_A, KeyEvent.META_DOWN_MASK); // Highlight every item in a document or window, or all characters in a text field (equivalent to the Select All command). See "The Edit Menu." 153 Shortcut.registerSystemShortcut("system:bold", "reserved", KeyEvent.VK_B, KeyEvent.META_DOWN_MASK); // Boldface the selected text or toggle boldfaced text on and off (equivalent to the Bold command). See "The Edit Menu." 154 Shortcut.registerSystemShortcut("system:copy", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK); // Duplicate the selected data and store on the Clipboard (equivalent to the Copy command). See "The Edit Menu." 155 Shortcut.registerSystemShortcut("system:colors", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display the Colors window (equivalent to the Show Colors command). See "The Format Menu." 156 Shortcut.registerSystemShortcut("system:copystyle", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Copy the style of the selected text (equivalent to the Copy Style command). See "The Format Menu." 157 Shortcut.registerSystemShortcut("system:copyformat", "reserved", KeyEvent.VK_C, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK).setAutomatic(); // Copy the formatting settings of the selected item and store on the Clipboard (equivalent to the Copy Ruler command). See "The Format Menu." 158 159 Shortcut.registerSystemShortcut("apple-reserved-42", "reserved", KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Show or hide the Dock. See "The Dock." 160 161 Shortcut.registerSystemShortcut("system:dictionarylookup", "reserved", KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK); // Display the definition of the selected word in the Dictionary application. 162 Shortcut.registerSystemShortcut("system:findselected", "reserved", KeyEvent.VK_E, KeyEvent.META_DOWN_MASK); // Use the selection for a find operation. See "Find Windows." 163 Shortcut.registerSystemShortcut("system:find", "reserved", KeyEvent.VK_F, KeyEvent.META_DOWN_MASK); // Open a Find window (equivalent to the Find command). See "The Edit Menu." 164 Shortcut.registerSystemShortcut("system:search", "reserved", KeyEvent.VK_F, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Jump to the search field control. See "Search Fields." 165 Shortcut.registerSystemShortcut("system:findnext", "reserved", KeyEvent.VK_G, KeyEvent.META_DOWN_MASK); // Find the next occurrence of the selection (equivalent to the Find Next command). See "The Edit Menu." 166 Shortcut.registerSystemShortcut("system:findprev", "reserved", KeyEvent.VK_G, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Find the previous occurrence of the selection (equivalent to the Find Previous command). See "The Edit Menu." 167 Shortcut.registerSystemShortcut("system:hide", "reserved", KeyEvent.VK_H, KeyEvent.META_DOWN_MASK).setAutomatic(); // Hide the windows of the currently running application (equivalent to the Hide ApplicationName command). See "The Application Menu." 168 Shortcut.registerSystemShortcut("system:hideothers", "reserved", KeyEvent.VK_H, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Hide the windows of all other running applications (equivalent to the Hide Others command). See "The Application Menu." 169 // What about applications that have italic text AND info windows? 170 //Shortcut.registerSystemCut("system:italic", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK); // Italicize the selected text or toggle italic text on or off (equivalent to the Italic command). See "The Format Menu." 171 Shortcut.registerSystemShortcut("system:info", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK); // Display an Info window. See "Inspector Windows." 172 Shortcut.registerSystemShortcut("system:inspector", "reserved", KeyEvent.VK_I, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Display an inspector window. See "Inspector Windows." 173 Shortcut.registerSystemShortcut("system:toselection", "reserved", KeyEvent.VK_J, KeyEvent.META_DOWN_MASK); // Scroll to a selection. 174 Shortcut.registerSystemShortcut("system:minimize", "reserved", KeyEvent.VK_M, KeyEvent.META_DOWN_MASK); // Minimize the active window to the Dock (equivalent to the Minimize command). See "The Window Menu." 175 Shortcut.registerSystemShortcut("system:minimizeall", "reserved", KeyEvent.VK_M, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Minimize all windows of the active application to the Dock (equivalent to the Minimize All command). See "The Window Menu." 176 Shortcut.registerSystemShortcut("system:new", "reserved", KeyEvent.VK_N, KeyEvent.META_DOWN_MASK); // Open a new document (equivalent to the New command). See "The File Menu." 177 Shortcut.registerSystemShortcut("system:open", "reserved", KeyEvent.VK_O, KeyEvent.META_DOWN_MASK); // Display a dialog for choosing a document to open (equivalent to the Open command). See "The File Menu." 178 Shortcut.registerSystemShortcut("system:print", "reserved", KeyEvent.VK_P, KeyEvent.META_DOWN_MASK); // Display the Print dialog (equivalent to the Print command). See "The File Menu." 179 Shortcut.registerSystemShortcut("system:printsetup", "reserved", KeyEvent.VK_P, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display a dialog for specifying printing parameters (equivalent to the Page Setup command). See "The File Menu." 180 Shortcut.registerSystemShortcut("system:menuexit", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK).setAutomatic(); // Quit the application (equivalent to the Quit command). See "The Application Menu." 181 182 Shortcut.registerSystemShortcut("apple-reserved-43", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Log out the current user (equivalent to the Log Out command). 183 Shortcut.registerSystemShortcut("apple-reserved-44", "reserved", KeyEvent.VK_Q, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK).setAutomatic(); // Log out the current user without confirmation. 184 185 Shortcut.registerSystemShortcut("system:save", "reserved", KeyEvent.VK_S, KeyEvent.META_DOWN_MASK); // Save the active document (equivalent to the Save command). See "The File Menu." 186 Shortcut.registerSystemShortcut("system:saveas", "reserved", KeyEvent.VK_S, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Display the Save dialog (equivalent to the Save As command). See "The File Menu." 187 Shortcut.registerSystemShortcut("system:fonts", "reserved", KeyEvent.VK_T, KeyEvent.META_DOWN_MASK); // Display the Fonts window (equivalent to the Show Fonts command). See "The Format Menu." 188 Shortcut.registerSystemShortcut("system:toggletoolbar", "reserved", KeyEvent.VK_T, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Show or hide a toolbar (equivalent to the Show/Hide Toolbar command). See "The View Menu" and "Toolbars." 189 Shortcut.registerSystemShortcut("system:underline", "reserved", KeyEvent.VK_U, KeyEvent.META_DOWN_MASK); // Underline the selected text or turn underlining on or off (equivalent to the Underline command). See "The Format Menu." 190 Shortcut.registerSystemShortcut("system:paste", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK); // Insert the Clipboard contents at the insertion point (equivalent to the Paste command). See "The File Menu." 191 Shortcut.registerSystemShortcut("system:pastestyle", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Apply the style of one object to the selected object (equivalent to the Paste Style command). See "The Format Menu." 192 Shortcut.registerSystemShortcut("system:pastemwithoutstyle", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Apply the style of the surrounding text to the inserted object (equivalent to the Paste and Match Style command). See "The Edit Menu." 193 Shortcut.registerSystemShortcut("system:pasteformatting", "reserved", KeyEvent.VK_V, KeyEvent.META_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK); // Apply formatting settings to the selected object (equivalent to the Paste Ruler command). See "The Format Menu." 194 Shortcut.registerSystemShortcut("system:closewindow", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK); // Close the active window (equivalent to the Close command). See "The File Menu." 195 Shortcut.registerSystemShortcut("system:closefile", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Close a file and its associated windows (equivalent to the Close File command). See "The File Menu." 196 Shortcut.registerSystemShortcut("system:closeallwindows", "reserved", KeyEvent.VK_W, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); // Close all windows in the application (equivalent to the Close All command). See "The File Menu." 197 Shortcut.registerSystemShortcut("system:cut", "reserved", KeyEvent.VK_X, KeyEvent.META_DOWN_MASK); // Remove the selection and store on the Clipboard (equivalent to the Cut command). See "The Edit Menu." 198 Shortcut.registerSystemShortcut("system:undo", "reserved", KeyEvent.VK_Z, KeyEvent.META_DOWN_MASK); // Reverse the effect of the user's previous operation (equivalent to the Undo command). See "The Edit Menu." 199 Shortcut.registerSystemShortcut("system:redo", "reserved", KeyEvent.VK_Z, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK); // Reverse the effect of the last Undo command (equivalent to the Redo command). See "The Edit Menu." 200 201 Shortcut.registerSystemShortcut("apple-reserved-45", "reserved", KeyEvent.VK_RIGHT, KeyEvent.META_DOWN_MASK).setAutomatic(); // Change the keyboard layout to current layout of Roman script. 202 //Shortcut.registerSystemCut("apple-reserved-46", "reserved", KeyEvent.VK_RIGHT, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the next semantic unit, typically the end of the current line. 203 //Shortcut.registerSystemCut("apple-reserved-47", "reserved", KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection one character to the right. 204 //Shortcut.registerSystemCut("apple-reserved-48", "reserved", KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the end of the current word, then to the end of the next word. 205 206 Shortcut.registerSystemShortcut("system:movefocusright", "reserved", KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview. 207 208 Shortcut.registerSystemShortcut("apple-reserved-49", "reserved", KeyEvent.VK_LEFT, KeyEvent.META_DOWN_MASK).setAutomatic(); // Change the keyboard layout to current layout of system script. 209 //Shortcut.registerSystemCut("apple-reserved-50", "reserved", KeyEvent.VK_LEFT, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the previous semantic unit, typically the beginning of the current line. 210 //Shortcut.registerSystemCut("apple-reserved-51", "reserved", KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection one character to the left. 211 //Shortcut.registerSystemCut("apple-reserved-52", "reserved", KeyEvent.VK_LEFT, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the beginning of the current word, then to the beginning of the previous word. 212 213 Shortcut.registerSystemShortcut("system:movefocusleft", "reserved", KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview. 214 215 //Shortcut.registerSystemCut("apple-reserved-53", "reserved", KeyEvent.VK_UP, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection upward in the next semantic unit, typically the beginning of the document. 216 //Shortcut.registerSystemCut("apple-reserved-54", "reserved", KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the line above, to the nearest character boundary at the same horizontal location. 217 //Shortcut.registerSystemCut("apple-reserved-55", "reserved", KeyEvent.VK_UP, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the beginning of the current paragraph, then to the beginning of the next paragraph. 218 219 Shortcut.registerSystemShortcut("system:movefocusup", "reserved", KeyEvent.VK_UP, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview. 220 221 //Shortcut.registerSystemCut("apple-reserved-56", "reserved", KeyEvent.VK_DOWN, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection downward in the next semantic unit, typically the end of the document. 222 //Shortcut.registerSystemCut("apple-reserved-57", "reserved", KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the line below, to the nearest character boundary at the same horizontal location. 223 //Shortcut.registerSystemCut("apple-reserved-58", "reserved", KeyEvent.VK_DOWN, KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK).setAutomatic(); // Extend selection to the end of the current paragraph, then to the end of the next paragraph (include the blank line between paragraphs in cut, copy, and paste operations). 224 225 Shortcut.registerSystemShortcut("system:movefocusdown", "reserved", KeyEvent.VK_DOWN, KeyEvent.CTRL_DOWN_MASK); // Move focus to another value or cell within a view, such as a table. See Accessibility Overview. 226 227 Shortcut.registerSystemShortcut("system:about", "reserved", 0, -1).setAutomatic(); // About 228 } 229 public String makeTooltip(String name, Shortcut sc) { 230 String lafid = UIManager.getLookAndFeel().getID(); 231 boolean canHtml = true; 232 // "Mac" is the native LAF, "Aqua" is Quaqua. Both use native menus with native tooltips. 233 if (lafid.contains("Mac") || lafid.contains("Aqua")) { 234 canHtml = false; 235 } 236 String result = ""; 237 if (canHtml) result += "<html>"; 238 result += name; 239 if (sc != null && sc.getKeyText().length() != 0) { 240 result += " "; 241 if (canHtml) result += "<font size='-2'>"; 242 result += "("+sc.getKeyText()+")"; 243 if (canHtml) result += "</font>"; 244 } 245 if (canHtml) result += " </html>"; 246 return result; 247 } 248 248 } -
trunk/src/org/openstreetmap/josm/tools/PlatformHookUnixoid.java
r1084 r1169 15 15 */ 16 16 public class PlatformHookUnixoid implements PlatformHook { 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 17 public void preStartupHook(){ 18 } 19 public void startupHook() { 20 } 21 public void openUrl(String url) throws IOException { 22 String[] programs = {"gnome-open", "kfmclient openURL", "firefox"}; 23 for (String program : programs) { 24 try { 25 Runtime.getRuntime().exec(program+" "+url); 26 return; 27 } catch (IOException e) { 28 } 29 } 30 } 31 public void initShortcutGroups() { 32 // This is the Windows list. Someone should look over it and make it more "*nix"... 33 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE), Integer.toString(-1)); 34 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 35 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 36 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT), Integer.toString(0)); 37 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 38 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT), Integer.toString(0)); 39 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK)); 40 40 41 42 43 44 45 46 47 41 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE), Integer.toString(-1)); 42 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 43 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 44 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK)); 45 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 46 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK)); 47 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 48 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 49 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE), Integer.toString(-1)); 50 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 51 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 52 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT), Integer.toString(KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 53 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 54 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 55 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 56 } 57 public void initSystemShortcuts() { 58 // TODO: Insert system shortcuts here. See Windows and espacially OSX to see how to. 59 } 60 /** 61 * This should work for all platforms. Yeah, should. 62 * See PlatformHook.java for a list of reasons why 63 * this is implemented here... 64 */ 65 public String makeTooltip(String name, Shortcut sc) { 66 String result = ""; 67 result += "<html>"; 68 result += name; 69 if (sc != null && sc.getKeyText().length() != 0) { 70 result += " "; 71 result += "<font size='-2'>"; 72 result += "("+sc.getKeyText()+")"; 73 result += "</font>"; 74 } 75 result += " </html>"; 76 return result; 77 } 78 78 } -
trunk/src/org/openstreetmap/josm/tools/PlatformHookWindows.java
r1084 r1169 14 14 */ 15 15 public class PlatformHookWindows extends PlatformHookUnixoid implements PlatformHook { 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 16 public void preStartupHook(){ 17 } 18 public void startupHook() { 19 } 20 public void openUrl(String url) throws IOException { 21 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 22 } 23 public void initShortcutGroups() { 24 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_NONE), Integer.toString(-1)); 25 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 26 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 27 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_EDIT), Integer.toString(0)); 28 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 29 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_DIRECT), Integer.toString(0)); 30 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_DEFAULT+Shortcut.GROUP_MNEMONIC),Integer.toString(KeyEvent.ALT_DOWN_MASK)); 31 31 32 33 34 35 36 37 38 32 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_NONE), Integer.toString(-1)); 33 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 34 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 35 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_EDIT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK)); 36 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 37 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_DIRECT), Integer.toString(KeyEvent.SHIFT_DOWN_MASK)); 38 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT1+Shortcut.GROUP_MNEMONIC), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 39 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 40 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_NONE), Integer.toString(-1)); 41 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_HOTKEY), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 42 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MENU), Integer.toString(KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK)); 43 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_EDIT), Integer.toString(KeyEvent.ALT_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 44 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_LAYER), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 45 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_DIRECT), Integer.toString(KeyEvent.CTRL_DOWN_MASK)); 46 Main.pref.put("shortcut.groups."+(Shortcut.GROUPS_ALT2+Shortcut.GROUP_MNEMONIC), Integer.toString(KeyEvent.ALT_DOWN_MASK)); 47 } 48 public void initSystemShortcuts() { 49 // This list if far from complete! 50 Shortcut.registerSystemShortcut("system:exit", "unused", KeyEvent.VK_F4, KeyEvent.ALT_DOWN_MASK).setAutomatic(); // items with automatic shortcuts will not be added to the menu bar at all 51 Shortcut.registerSystemShortcut("system:menuexit", "unused", KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK); 52 Shortcut.registerSystemShortcut("system:copy", "unused", KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK); 53 Shortcut.registerSystemShortcut("system:paste", "unused", KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK); 54 Shortcut.registerSystemShortcut("system:cut", "unused", KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK); 55 Shortcut.registerSystemShortcut("system:duplicate", "unused", KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK); // not really system, but to avoid odd results 56 Shortcut.registerSystemShortcut("system:help", "unused", KeyEvent.VK_F1, 0); 57 } 58 58 } 59 59 -
trunk/src/org/openstreetmap/josm/tools/PrimaryDateParser.java
r627 r1169 15 15 * based on similar code in JOSM. This class is not threadsafe, a separate 16 16 * instance must be created per thread. 17 * 17 * 18 18 * @author Brett Henderson 19 19 */ 20 20 public class PrimaryDateParser { 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 * 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 21 private DatatypeFactory datatypeFactory; 22 private FallbackDateParser fallbackDateParser; 23 private Calendar calendar; 24 25 26 /** 27 * Creates a new instance. 28 */ 29 public PrimaryDateParser() { 30 // Build an xml data type factory. 31 try { 32 datatypeFactory = DatatypeFactory.newInstance(); 33 34 } catch (DatatypeConfigurationException e) { 35 throw new RuntimeException("Unable to instantiate xml datatype factory.", e); 36 } 37 38 fallbackDateParser = new FallbackDateParser(); 39 40 calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 41 } 42 43 44 private boolean isDateInShortStandardFormat(String date) { 45 char[] dateChars; 46 // We can only parse the date if it is in a very specific format. 47 // eg. 2007-09-23T08:25:43Z 48 49 if (date.length() != 20) { 50 return false; 51 } 52 53 dateChars = date.toCharArray(); 54 55 // Make sure any fixed characters are in the correct place. 56 if (dateChars[4] != '-') { 57 return false; 58 } 59 if (dateChars[7] != '-') { 60 return false; 61 } 62 if (dateChars[10] != 'T') { 63 return false; 64 } 65 if (dateChars[13] != ':') { 66 return false; 67 } 68 if (dateChars[16] != ':') { 69 return false; 70 } 71 if (dateChars[19] != 'Z') { 72 return false; 73 } 74 75 // Ensure all remaining characters are numbers. 76 for (int i = 0; i < 4; i++) { 77 if (dateChars[i] < '0' || dateChars[i] > '9') { 78 return false; 79 } 80 } 81 for (int i = 5; i < 7; i++) { 82 if (dateChars[i] < '0' || dateChars[i] > '9') { 83 return false; 84 } 85 } 86 for (int i = 8; i < 10; i++) { 87 if (dateChars[i] < '0' || dateChars[i] > '9') { 88 return false; 89 } 90 } 91 for (int i = 11; i < 13; i++) { 92 if (dateChars[i] < '0' || dateChars[i] > '9') { 93 return false; 94 } 95 } 96 for (int i = 14; i < 16; i++) { 97 if (dateChars[i] < '0' || dateChars[i] > '9') { 98 return false; 99 } 100 } 101 for (int i = 17; i < 19; i++) { 102 if (dateChars[i] < '0' || dateChars[i] > '9') { 103 return false; 104 } 105 } 106 107 // No problems found so it is in the special case format. 108 return true; 109 } 110 111 112 private boolean isDateInLongStandardFormat(String date) { 113 char[] dateChars; 114 // We can only parse the date if it is in a very specific format. 115 // eg. 2007-09-23T08:25:43.000Z 116 117 if (date.length() != 24) { 118 return false; 119 } 120 121 dateChars = date.toCharArray(); 122 123 // Make sure any fixed characters are in the correct place. 124 if (dateChars[4] != '-') { 125 return false; 126 } 127 if (dateChars[7] != '-') { 128 return false; 129 } 130 if (dateChars[10] != 'T') { 131 return false; 132 } 133 if (dateChars[13] != ':') { 134 return false; 135 } 136 if (dateChars[16] != ':') { 137 return false; 138 } 139 if (dateChars[19] != '.') { 140 return false; 141 } 142 if (dateChars[23] != 'Z') { 143 return false; 144 } 145 146 // Ensure all remaining characters are numbers. 147 for (int i = 0; i < 4; i++) { 148 if (dateChars[i] < '0' || dateChars[i] > '9') { 149 return false; 150 } 151 } 152 for (int i = 5; i < 7; i++) { 153 if (dateChars[i] < '0' || dateChars[i] > '9') { 154 return false; 155 } 156 } 157 for (int i = 8; i < 10; i++) { 158 if (dateChars[i] < '0' || dateChars[i] > '9') { 159 return false; 160 } 161 } 162 for (int i = 11; i < 13; i++) { 163 if (dateChars[i] < '0' || dateChars[i] > '9') { 164 return false; 165 } 166 } 167 for (int i = 14; i < 16; i++) { 168 if (dateChars[i] < '0' || dateChars[i] > '9') { 169 return false; 170 } 171 } 172 for (int i = 17; i < 19; i++) { 173 if (dateChars[i] < '0' || dateChars[i] > '9') { 174 return false; 175 } 176 } 177 for (int i = 20; i < 23; i++) { 178 if (dateChars[i] < '0' || dateChars[i] > '9') { 179 return false; 180 } 181 } 182 183 // No problems found so it is in the special case format. 184 return true; 185 } 186 187 188 private Date parseShortStandardDate(String date) { 189 int year; 190 int month; 191 int day; 192 int hour; 193 int minute; 194 int second; 195 196 year = Integer.parseInt(date.substring(0, 4)); 197 month = Integer.parseInt(date.substring(5, 7)); 198 day = Integer.parseInt(date.substring(8, 10)); 199 hour = Integer.parseInt(date.substring(11, 13)); 200 minute = Integer.parseInt(date.substring(14, 16)); 201 second = Integer.parseInt(date.substring(17, 19)); 202 203 calendar.clear(); 204 calendar.set(Calendar.YEAR, year); 205 calendar.set(Calendar.MONTH, month - 1); 206 calendar.set(Calendar.DAY_OF_MONTH, day); 207 calendar.set(Calendar.HOUR_OF_DAY, hour); 208 calendar.set(Calendar.MINUTE, minute); 209 calendar.set(Calendar.SECOND, second); 210 211 return calendar.getTime(); 212 } 213 214 215 private Date parseLongStandardDate(String date) { 216 int year; 217 int month; 218 int day; 219 int hour; 220 int minute; 221 int second; 222 int millisecond; 223 224 year = Integer.parseInt(date.substring(0, 4)); 225 month = Integer.parseInt(date.substring(5, 7)); 226 day = Integer.parseInt(date.substring(8, 10)); 227 hour = Integer.parseInt(date.substring(11, 13)); 228 minute = Integer.parseInt(date.substring(14, 16)); 229 second = Integer.parseInt(date.substring(17, 19)); 230 millisecond = Integer.parseInt(date.substring(20, 23)); 231 232 calendar.clear(); 233 calendar.set(Calendar.YEAR, year); 234 calendar.set(Calendar.MONTH, month - 1); 235 calendar.set(Calendar.DAY_OF_MONTH, day); 236 calendar.set(Calendar.HOUR_OF_DAY, hour); 237 calendar.set(Calendar.MINUTE, minute); 238 calendar.set(Calendar.SECOND, second); 239 calendar.set(Calendar.MILLISECOND, millisecond); 240 241 return calendar.getTime(); 242 } 243 244 245 /** 246 * Attempts to parse the specified date. 247 * 248 * @param date 249 * The date to parse. 250 * @return The date. 251 * @throws ParseException 252 * Occurs if the date does not match any of the supported date 253 * formats. 254 */ 255 public Date parse(String date) throws ParseException { 256 try { 257 if (isDateInShortStandardFormat(date)) { 258 return parseShortStandardDate(date); 259 } else if (isDateInLongStandardFormat(date)) { 260 return parseLongStandardDate(date); 261 } else { 262 return datatypeFactory.newXMLGregorianCalendar(date).toGregorianCalendar().getTime(); 263 } 264 265 } catch (IllegalArgumentException e) { 266 return fallbackDateParser.parse(date); 267 } 268 } 269 269 } -
trunk/src/org/openstreetmap/josm/tools/Shortcut.java
r1157 r1169 139 139 */ 140 140 public KeyStroke getKeyStroke() { 141 if (assignedModifier != -1) 142 return KeyStroke.getKeyStroke(assignedKey, assignedModifier); 141 if (assignedModifier != -1) 142 return KeyStroke.getKeyStroke(assignedKey, assignedModifier); 143 143 return null; 144 144 } … … 351 351 System.err.println("CONFLICT WITH SYSTEM KEY "+shortText); 352 352 return null; 353 } 353 } 354 354 potentialShortcut = new Shortcut(shortText, longText, key, GROUP_RESERVED, key, modifier, true, false); 355 355 shortcuts.put(shortText, potentialShortcut); … … 441 441 // a lengthy warning message 442 442 private static void displayWarning(Shortcut conflictsWith, Shortcut potentialShortcut, String shortText, String longText) { 443 JOptionPane.showMessageDialog(Main.parent, 443 JOptionPane.showMessageDialog(Main.parent, 444 444 tr("Setting the keyboard shortcut ''{0}'' for the action ''{1}'' ({2}) failed\n"+ 445 445 "because the shortcut is already taken by the action ''{3}'' ({4}).\n\n", -
trunk/src/org/openstreetmap/josm/tools/UrlLabel.java
r627 r1169 12 12 public class UrlLabel extends JEditorPane implements HyperlinkListener { 13 13 14 14 private final String url; 15 15 16 17 18 16 public UrlLabel(String url) { 17 this (url, url); 18 } 19 19 20 21 22 23 24 25 26 27 28 20 public UrlLabel(String url, String description) { 21 this.url = url; 22 setContentType("text/html"); 23 setText("<html><a href=\""+url+"\">"+description+"</a></html>"); 24 setToolTipText(url); 25 setEditable(false); 26 setOpaque(false); 27 addHyperlinkListener(this); 28 } 29 29 30 31 32 33 34 30 public void hyperlinkUpdate(HyperlinkEvent e) { 31 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 32 OpenBrowser.displayUrl(url); 33 } 34 } 35 35 } -
trunk/src/org/openstreetmap/josm/tools/WikiReader.java
r740 r1169 14 14 public class WikiReader { 15 15 16 17 16 public static final String JOSM_EXTERN = "http://josm-extern."; 17 private final String baseurl; 18 18 19 20 19 public WikiReader(String baseurl) { 20 this.baseurl = baseurl; 21 21 } 22 22 23 24 25 26 27 28 29 30 31 32 23 /** 24 * Read the page specified by the url and return the content. 25 * 26 * If the url is within the baseurl path, parse it as an trac wikipage and 27 * replace relative pathes etc.. 28 * 29 * @return Either the string of the content of the wiki page. 30 * @throws IOException Throws, if the page could not be loaded. 31 */ 32 public String read(String url) throws IOException { 33 33 BufferedReader in = new BufferedReader(new InputStreamReader(new URL(url).openStream(), "utf-8")); 34 34 if (url.startsWith(baseurl)) 35 35 return readFromTrac(in, url); 36 36 return readNormal(in); 37 }38 39 private String readNormal(BufferedReader in) throws IOException {40 StringBuilder b = new StringBuilder("<html>");41 for (String line = in.readLine(); line != null; line = in.readLine()) {42 line = adjustText(line);43 b.append(line);44 b.append("\n");45 }46 return b.toString();47 37 } 48 38 49 private String readFromTrac(BufferedReader in, String url) throws IOException { 39 private String readNormal(BufferedReader in) throws IOException { 40 StringBuilder b = new StringBuilder("<html>"); 41 for (String line = in.readLine(); line != null; line = in.readLine()) { 42 line = adjustText(line); 43 b.append(line); 44 b.append("\n"); 45 } 46 return b.toString(); 47 } 48 49 private String readFromTrac(BufferedReader in, String url) throws IOException { 50 50 boolean inside = false; 51 51 StringBuilder b = new StringBuilder("<html>"); 52 52 for (String line = in.readLine(); line != null; line = in.readLine()) { 53 54 55 56 57 58 59 60 61 62 63 64 65 53 if (line.contains("<div id=\"searchable\">")) 54 inside = true; 55 else if (line.contains("<div class=\"buttons\">")) 56 inside = false; 57 if (inside) { 58 line = line.replaceAll("<img src=\"/", "<img src=\""+baseurl+"/"); 59 line = line.replaceAll("href=\"/", "href=\""+baseurl+"/"); 60 if (!line.contains("$")) 61 line = line.replaceAll("<p>Describe \"([^\"]+)\" here</p>", "<p>Describe \"$1\" <a href=\""+JOSM_EXTERN+url.substring(7)+"\">here</a></p>"); 62 line = adjustText(line); 63 b.append(line); 64 b.append("\n"); 65 } 66 66 } 67 67 b.append("</html>"); … … 69 69 } 70 70 71 72 73 71 private String adjustText(String text) { 72 text = text.replaceAll(" />", ">"); 73 return text; 74 74 } 75 75 } -
trunk/src/org/openstreetmap/josm/tools/XmlObjectParser.java
r1163 r1169 31 31 32 32 public static final String lang = Main.getLanguageCode(); 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 return s != null && 155 !s.equals("0") && 156 !s.startsWith("off") && 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 } 259 260 261 33 public static class Uniform<T> implements Iterable<T>{ 34 private Iterator<Object> iterator; 35 /** 36 * @param klass This has to be specified since generics are ereased from 37 * class files so the JVM cannot deduce T itself. 38 */ 39 public Uniform(Reader input, String tagname, Class<T> klass) { 40 XmlObjectParser parser = new XmlObjectParser(); 41 parser.map(tagname, klass); 42 parser.start(input); 43 iterator = parser.iterator(); 44 } 45 public Iterator<T> iterator() { 46 return new Iterator<T>(){ 47 public boolean hasNext() {return iterator.hasNext();} 48 @SuppressWarnings("unchecked") public T next() {return (T)iterator.next();} 49 public void remove() {iterator.remove();} 50 }; 51 } 52 } 53 54 private class Parser extends DefaultHandler { 55 Stack<Object> current = new Stack<Object>(); 56 String characters = ""; 57 @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException { 58 if (mapping.containsKey(qname)) { 59 Class<?> klass = mapping.get(qname).klass; 60 try { 61 current.push(klass.newInstance()); 62 } catch (Exception e) { 63 throw new SAXException(e); 64 } 65 for (int i = 0; i < a.getLength(); ++i) 66 setValue(a.getQName(i), a.getValue(i)); 67 if (mapping.get(qname).onStart) 68 report(); 69 if (mapping.get(qname).both) 70 { 71 try { 72 queue.put(current.peek()); 73 } catch (InterruptedException e) { 74 } 75 } 76 } 77 } 78 @Override public void endElement(String ns, String lname, String qname) throws SAXException { 79 if (mapping.containsKey(qname) && !mapping.get(qname).onStart) 80 report(); 81 else if (characters != null && !current.isEmpty()) { 82 setValue(qname, characters.trim()); 83 characters = ""; 84 } 85 } 86 @Override public void characters(char[] ch, int start, int length) { 87 String s = new String(ch, start, length); 88 characters += s; 89 } 90 91 private void report() { 92 try { 93 queue.put(current.pop()); 94 } catch (InterruptedException e) { 95 } 96 characters = ""; 97 } 98 99 private Object getValueForClass(Class<?> klass, String value) { 100 if (klass == Boolean.TYPE) 101 return parseBoolean(value); 102 else if (klass == Integer.TYPE || klass == Long.TYPE) 103 return Long.parseLong(value); 104 else if (klass == Float.TYPE || klass == Double.TYPE) 105 return Double.parseDouble(value); 106 return value; 107 } 108 109 private void setValue(String fieldName, String value) throws SAXException { 110 if (fieldName.equals("class") || fieldName.equals("default") || fieldName.equals("throw") || fieldName.equals("new") || fieldName.equals("null")) 111 fieldName += "_"; 112 try { 113 Object c = current.peek(); 114 Field f = null; 115 try { 116 f = c.getClass().getField(fieldName); 117 } catch (NoSuchFieldException e) { 118 if(fieldName.startsWith(lang)) 119 { 120 String locfieldName = "locale_" + 121 fieldName.substring(lang.length()); 122 try { 123 f = c.getClass().getField(locfieldName); 124 } catch (NoSuchFieldException ex) { 125 } 126 } 127 } 128 if (f != null && Modifier.isPublic(f.getModifiers())) 129 f.set(c, getValueForClass(f.getType(), value)); 130 else { 131 if(fieldName.startsWith(lang)) 132 { 133 int l = lang.length(); 134 fieldName = "set" + fieldName.substring(l,l+1).toUpperCase() + fieldName.substring(l+1); 135 } 136 else 137 { 138 fieldName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1); 139 } 140 Method[] methods = c.getClass().getDeclaredMethods(); 141 for (Method m : methods) { 142 if (m.getName().equals(fieldName) && m.getParameterTypes().length == 1) { 143 m.invoke(c, new Object[]{getValueForClass(m.getParameterTypes()[0], value)}); 144 return; 145 } 146 } 147 } 148 } catch (Exception e) { 149 e.printStackTrace(); // SAXException does not dump inner exceptions. 150 throw new SAXException(e); 151 } 152 } 153 private boolean parseBoolean(String s) { 154 return s != null && 155 !s.equals("0") && 156 !s.startsWith("off") && 157 !s.startsWith("false") && 158 !s.startsWith("no"); 159 } 160 } 161 162 private static class Entry { 163 Class<?> klass; 164 boolean onStart; 165 boolean both; 166 public Entry(Class<?> klass, boolean onStart, boolean both) { 167 super(); 168 this.klass = klass; 169 this.onStart = onStart; 170 this.both = both; 171 } 172 } 173 174 private Map<String, Entry> mapping = new HashMap<String, Entry>(); 175 private Parser parser; 176 177 /** 178 * The queue of already parsed items from the parsing thread. 179 */ 180 private BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10); 181 182 /** 183 * This stores one item retrieved from the queue to give hasNext a chance. 184 * So this is also the object that will be returned on the next call to next(). 185 */ 186 private Object lookAhead = null; 187 188 /** 189 * This object represent the end of the stream (null is not allowed as 190 * member in class Queue). 191 */ 192 private Object EOS = new Object(); 193 194 public XmlObjectParser() { 195 parser = new Parser(); 196 } 197 198 public Iterable<Object> start(final Reader in) { 199 new Thread(){ 200 @Override public void run() { 201 try { 202 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(in), parser); 203 } catch (Exception e) { 204 try { 205 queue.put(e); 206 } catch (InterruptedException e1) { 207 } 208 } 209 parser = null; 210 try { 211 queue.put(EOS); 212 } catch (InterruptedException e) { 213 } 214 } 215 }.start(); 216 return this; 217 } 218 219 public void map(String tagName, Class<?> klass) { 220 mapping.put(tagName, new Entry(klass,false,false)); 221 } 222 223 public void mapOnStart(String tagName, Class<?> klass) { 224 mapping.put(tagName, new Entry(klass,true,false)); 225 } 226 227 public void mapBoth(String tagName, Class<?> klass) { 228 mapping.put(tagName, new Entry(klass,false,true)); 229 } 230 231 /** 232 * @return The next object from the xml stream or <code>null</code>, 233 * if no more objects. 234 */ 235 public Object next() throws SAXException { 236 fillLookAhead(); 237 if (lookAhead == EOS) 238 throw new NoSuchElementException(); 239 Object o = lookAhead; 240 lookAhead = null; 241 return o; 242 } 243 244 private void fillLookAhead() throws SAXException { 245 if (lookAhead != null) 246 return; 247 try { 248 lookAhead = queue.take(); 249 if (lookAhead instanceof SAXException) 250 throw (SAXException)lookAhead; 251 else if (lookAhead instanceof RuntimeException) 252 throw (RuntimeException)lookAhead; 253 else if (lookAhead instanceof Exception) 254 throw new SAXException((Exception)lookAhead); 255 } catch (InterruptedException e) { 256 throw new RuntimeException("XmlObjectParser must not be interrupted.", e); 257 } 258 } 259 260 public boolean hasNext() throws SAXException { 261 fillLookAhead(); 262 262 return lookAhead != EOS; 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 263 } 264 265 public Iterator<Object> iterator() { 266 return new Iterator<Object>(){ 267 public boolean hasNext() { 268 try { 269 return XmlObjectParser.this.hasNext(); 270 } catch (SAXException e) { 271 e.printStackTrace(); 272 throw new RuntimeException(e); 273 } 274 } 275 public Object next() { 276 try { 277 return XmlObjectParser.this.next(); 278 } catch (SAXException e) { 279 e.printStackTrace(); 280 throw new RuntimeException(e); 281 } 282 } 283 public void remove() { 284 throw new UnsupportedOperationException(); 285 } 286 }; 287 } 288 288 }
Note:
See TracChangeset
for help on using the changeset viewer.