Changeset 31927 in osm for applications/editors/josm
- Timestamp:
- 2016-01-03T16:18:19+01:00 (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/CommandLine/src/CommandLine/CommandLine.java
r31869 r31927 30 30 import java.awt.event.MouseEvent; 31 31 import java.io.File; 32 import java.io.FileOutputStream;33 32 import java.io.IOException; 34 33 import java.io.InputStream; … … 38 37 import java.net.URL; 39 38 import java.nio.charset.StandardCharsets; 39 import java.nio.file.Files; 40 import java.nio.file.StandardCopyOption; 40 41 import java.util.ArrayList; 41 42 import java.util.Collection; … … 77 78 78 79 public class CommandLine extends Plugin { 79 protected JTextField textField; 80 protected JTextField historyField; 81 private String prefix; 82 private Mode mode; 83 private ArrayList<Command> commands; 84 private JMenu commandMenu; 85 protected Command currentCommand; 86 protected String commandSymbol; 87 protected History history; 88 protected MapFrame currentMapFrame; 89 protected MapMode previousMode; 90 91 static final String pluginDir = Main.pref.getPluginsDirectory().getAbsolutePath() + "/CommandLine/"; 92 93 @SuppressWarnings("serial") 94 public CommandLine(PluginInformation info) { 95 super(info); 96 commandSymbol = ": "; 97 history = new History(100); 98 historyField = new DisableShortcutsOnFocusGainedTextField(); 99 textField = new DisableShortcutsOnFocusGainedTextField() { 100 @Override 101 protected void processKeyEvent(KeyEvent e) { 102 if (e.getID() == KeyEvent.KEY_PRESSED) { 103 int code = e.getKeyCode(); 104 if (code == KeyEvent.VK_ENTER) { 105 String commandText = textField.getText().substring(prefix.length()); 106 switch (mode) { 107 case IDLE: 108 if (commandText.isEmpty()) { 109 commandText = history.getLastItem(); 110 } else { 111 history.addItem(commandText); 112 } 113 Command command = findCommand(commandText, true); 114 if (command != null) { 115 startCommand(command); 116 } else { 117 setMode(Mode.IDLE); 118 } 119 break; 120 case SELECTION: 121 if (currentMapFrame.mapMode instanceof WayAction || currentMapFrame.mapMode instanceof NodeAction || currentMapFrame.mapMode instanceof RelationAction || currentMapFrame.mapMode instanceof AnyAction) { 122 Collection<OsmPrimitive> selected = Main.main.getCurrentDataSet().getSelected(); 123 if (selected.size() > 0) 124 loadParameter(selected, true); 125 } 126 else { 127 loadParameter(commandText, currentCommand.parameters.get(currentCommand.currentParameterNum).maxInstances == 1); 128 } 129 break; 130 case ADJUSTMENT: 131 break; 132 } 133 e.consume(); 134 } 135 else if (code == KeyEvent.VK_UP) { 136 textField.setText(prefix + history.getPrevItem()); 137 e.consume(); 138 } 139 else if (code == KeyEvent.VK_DOWN) { 140 textField.setText(prefix + history.getNextItem()); 141 e.consume(); 142 } 143 else if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) { 144 if (textField.getCaretPosition() <= prefix.length()) 145 e.consume(); 146 } 147 else if (code == KeyEvent.VK_HOME) { 148 setCaretPosition(prefix.length()); 149 e.consume(); 150 } 151 else if (code == KeyEvent.VK_ESCAPE) { 152 if (textField.getText().length() == prefix.length() && mode == Mode.IDLE) 153 deactivate(); 154 else 155 endInput(); 156 e.consume(); 157 } 158 else if (code == KeyEvent.VK_DELETE || code == KeyEvent.VK_RIGHT || code == KeyEvent.VK_END) { 159 } 160 else { 161 e.consume(); 162 } 163 if (textField.getCaretPosition() < prefix.length() || (textField.getSelectionStart() < prefix.length() && textField.getSelectionStart() > 0) ) 164 e.consume(); 165 } 166 if (e.getID() == KeyEvent.KEY_TYPED) 167 if (textField.getCaretPosition() < prefix.length() || (textField.getSelectionStart() < prefix.length() && textField.getSelectionStart() > 0) ) 168 e.consume(); 169 super.processKeyEvent(e); 170 if (textField.getText().length() < prefix.length()) { // Safe 171 setMode(mode); 172 } 173 if (e.getID() == KeyEvent.KEY_TYPED) { 174 if (e.getKeyChar() > 'A' && e.getKeyChar() < 'z') { 175 Command command = findCommand(textField.getText().substring(prefix.length()), false); 176 if (command != null) { 177 int currentPos = textField.getSelectionStart() == 0 ? textField.getCaretPosition() : textField.getSelectionStart(); 178 textField.setText(prefix + command.name); 179 textField.setCaretPosition(currentPos); 180 textField.select(currentPos, prefix.length() + command.name.length()); 181 } 182 } 183 } 184 } 185 @Override 186 protected void processMouseEvent(MouseEvent e) { 187 super.processMouseEvent(e); 188 if (e.getButton() == MouseEvent.BUTTON1 && e.getID() == MouseEvent.MOUSE_RELEASED) { 189 if (textField.getSelectionStart() > 0 && textField.getSelectionStart() < prefix.length()) 190 textField.setSelectionStart(prefix.length()); 191 else if (textField.getCaretPosition() < prefix.length()) 192 textField.setCaretPosition(prefix.length()); 193 } 194 } 195 }; 196 197 if (Main.main.menu != null) { 198 commandMenu = Main.main.menu.addMenu("Commands", tr("Commands"), KeyEvent.VK_O, Main.main.menu.getDefaultMenuPos(), ht("/Plugin/CommandLine")); 199 MainMenu.add(commandMenu, new CommandLineAction(this)); 200 } 201 loadCommands(); 202 setMode(Mode.IDLE); 203 } 204 205 public void startCommand(String commandName) { 206 Command command = findCommand(commandName, true); 207 if (command != null) { 208 startCommand(command); 209 } 210 } 211 212 protected void startCommand(Command command) { 213 if (Main.map == null) 214 return; 215 DataSet ds = Main.main.getCurrentDataSet(); 216 if (ds == null) 217 return; 218 currentCommand = command; 219 currentCommand.resetLoading(); 220 parseSelection(ds.getSelected()); 221 if (!(Main.map.mapMode instanceof AnyAction || Main.map.mapMode instanceof DummyAction || Main.map.mapMode instanceof LengthAction || Main.map.mapMode instanceof NodeAction || Main.map.mapMode instanceof PointAction || Main.map.mapMode instanceof RelationAction || Main.map.mapMode instanceof WayAction)) { 222 previousMode = Main.map.mapMode; 223 } 224 if (currentCommand.currentParameterNum < currentCommand.parameters.size()) 225 setMode(Mode.SELECTION); 226 else 227 runTool(); 228 } 229 230 @Override 231 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 232 currentMapFrame = newFrame; 233 if (oldFrame == null && newFrame != null) { 234 JToolBar tb = new JToolBar(); 235 tb.setLayout(new BorderLayout()); 236 tb.setFloatable(false); 237 tb.setOrientation(JToolBar.HORIZONTAL); 238 tb.add(historyField, BorderLayout.NORTH); 239 tb.add(textField, BorderLayout.SOUTH); 240 currentMapFrame.add(tb, BorderLayout.NORTH); 241 printHistory("Loaded CommandLine, version " + getPluginInformation().version); 242 } 243 } 244 245 protected void printHistory(final String text) { 246 SwingUtilities.invokeLater(new Runnable() { 247 @Override 248 public void run() { 249 historyField.setText(text); 250 } 251 }); 252 } 253 254 private void loadCommands() { 255 commands = (new Loader(getPluginDir())).load(); 256 if (commands.isEmpty()) { 257 if (!GraphicsEnvironment.isHeadless() && JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Main.parent, 258 tr("No command has been found. Would you like to download and install default commands now?"), 259 tr("No command found"), JOptionPane.YES_NO_CANCEL_OPTION)) { 260 try { 261 downloadAndInstallDefaultCommands(); 262 commands = (new Loader(getPluginDir())).load(); 263 JOptionPane.showMessageDialog(Main.parent, tr("Default commands have been successfully installed"), 264 tr("Success"), JOptionPane.INFORMATION_MESSAGE); 265 } catch (IOException e) { 266 Main.warn(e); 267 JOptionPane.showMessageDialog(Main.parent, 268 tr("Failed to download and install default commands.\n\nError: {0}", e.getMessage()), 269 tr("Warning"), JOptionPane.WARNING_MESSAGE); 270 } 271 } 272 } 273 for (Command command : commands) { 274 commandMenu.add(new CommandAction(command, this)); 275 } 276 } 277 278 private void downloadAndInstallDefaultCommands() throws IOException { 279 String url = Main.pref.get("commandline.default.commands.url", 280 "https://github.com/Foxhind/JOSM-CommandLine-commands/archive/master.zip"); 281 try (ZipInputStream zis = new ZipInputStream(HttpClient.create(new URL(url)).connect().getContent(), StandardCharsets.UTF_8)) { 282 File dir = new File(getPluginDir()); 283 if (!dir.exists()) { 284 dir.mkdirs(); 285 } 286 ZipEntry entry = null; 287 while ( (entry = zis.getNextEntry()) != null ) { 288 if (!entry.isDirectory()) { 289 String name = entry.getName(); 290 if (name.contains("/")) { 291 name = name.substring(name.lastIndexOf("/")); 292 } 293 File file = new File(dir + File.separator + name); 294 Main.info("Installing command file: "+file); 295 if (!file.createNewFile()) { 296 throw new IOException("Could not create file: " + file.getAbsolutePath()); 297 } 298 // Write file 299 try (FileOutputStream fos = new FileOutputStream(file)) { 300 Utils.copyStream(zis, fos); 301 } 302 // Set last modification date 303 long time = entry.getTime(); 304 if (time > -1) { 305 file.setLastModified(time); 306 } 307 } 308 } 309 } 310 } 311 312 private Command findCommand(String text, boolean strict) { 313 for (int i = 0; i < commands.size(); i++) { 314 if (strict) { 315 if ( commands.get(i).name.equalsIgnoreCase(text) ) { 316 return commands.get(i); 317 } 318 } else if ( commands.get(i).name.toLowerCase().startsWith( text.toLowerCase() ) && text.length() > 1 ) { 319 return commands.get(i); 320 } 321 } 322 return null; 323 } 324 325 protected void setMode(Mode targetMode) { 326 DataSet currentDataSet = Main.main.getCurrentDataSet(); 327 if (currentDataSet != null) { 328 currentDataSet.clearSelection(); 329 Main.map.mapView.repaint(); 330 } 331 if (targetMode == Mode.IDLE) { 332 mode = Mode.IDLE; 333 currentCommand = null; 334 prefix = tr("Command") + commandSymbol; 335 textField.setText(prefix); 336 } 337 else if (targetMode == Mode.SELECTION) { 338 mode = Mode.SELECTION; 339 Parameter currentParameter = currentCommand.parameters.get(currentCommand.currentParameterNum); 340 prefix = tr(currentParameter.description == null ? currentParameter.name : currentParameter.description); 341 if (currentParameter.getRawValue() instanceof Relay) 342 prefix = prefix + " (" + ((Relay)(currentParameter.getRawValue())).getOptionsString() + ")"; 343 prefix += commandSymbol; 344 String value = currentParameter.getValue(); 345 textField.setText(prefix + value); 346 Type currentType = currentParameter.type; 347 MapMode action = null; 348 switch (currentType) { 349 case POINT: 350 action = new PointAction(currentMapFrame, this); 351 break; 352 case WAY: 353 action = new WayAction(currentMapFrame, this); 354 break; 355 case NODE: 356 action = new NodeAction(currentMapFrame, this); 357 break; 358 case RELATION: 359 action = new RelationAction(currentMapFrame, this); 360 break; 361 case ANY: 362 action = new AnyAction(currentMapFrame, this); 363 break; 364 case LENGTH: 365 action = new LengthAction(currentMapFrame, this); 366 break; 367 case USERNAME: 368 loadParameter(Main.pref.get("osm-server.username", null), true); 369 action = new DummyAction(currentMapFrame, this); 370 break; 371 case IMAGERYURL: 372 Layer layer = Main.map.mapView.getActiveLayer(); 373 if (layer != null) { 374 if (!(layer instanceof ImageryLayer)) { 375 List<ImageryLayer> imageryLayers = Main.map.mapView.getLayersOfType(ImageryLayer.class); 376 if (imageryLayers.size() == 1) { 377 layer = imageryLayers.get(0); 378 } 379 else { 380 endInput(); 381 return; 382 } 383 } 384 } 385 ImageryInfo info = ((ImageryLayer)layer).getInfo(); 386 String url = info.getUrl(); 387 String itype = info.getImageryType().getTypeString(); 388 loadParameter((url.equals("") ? itype : url), true); 389 action = new DummyAction(currentMapFrame, this); 390 break; 391 case IMAGERYOFFSET: 392 Layer olayer = Main.map.mapView.getActiveLayer(); 393 if (olayer != null) { 394 if (!(olayer instanceof ImageryLayer)) { 395 List<ImageryLayer> imageryLayers = Main.map.mapView.getLayersOfType(ImageryLayer.class); 396 if (imageryLayers.size() == 1) { 397 olayer = imageryLayers.get(0); 398 } 399 else { 400 endInput(); 401 return; 402 } 403 } 404 } 405 loadParameter((String.valueOf(((ImageryLayer)olayer).getDx()) + "," + String.valueOf(((ImageryLayer)olayer).getDy())), true); 406 action = new DummyAction(currentMapFrame, this); 407 break; 408 default: 409 action = new DummyAction(currentMapFrame, this); 410 break; 411 } 412 currentMapFrame.selectMapMode(action); 413 activate(); 414 textField.select(prefix.length(), textField.getText().length()); 415 } 416 else if (targetMode == Mode.PROCESSING) { 417 mode = Mode.PROCESSING; 418 prefix = tr("Processing..."); 419 textField.setText(prefix); 420 Main.map.mapView.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 421 } 422 } 423 424 public void activate() { 425 textField.requestFocus(); 426 textField.setCaretPosition(textField.getText().length()); 427 } 428 429 public void deactivate() { 430 Main.map.mapView.requestFocus(); 431 } 432 433 public void abortInput() { 434 printHistory(tr("Aborted") + "."); 435 endInput(); 436 } 437 438 public void endInput() { 439 setMode(Mode.IDLE); 440 Main.map.selectMapMode(previousMode); 441 Main.map.mapView.repaint(); 442 } 443 444 public void loadParameter(Object obj, boolean next) { 445 if (currentCommand.loadObject(obj)) { 446 if (currentCommand.hasNextParameter()) { 447 if (next) { 448 Parameter currentParameter = currentCommand.parameters.get(currentCommand.currentParameterNum); 449 String prefix = tr(currentParameter.description == null ? currentParameter.name : currentParameter.description); 450 prefix += commandSymbol; 451 String value = currentParameter.getValue(); 452 printHistory(prefix + value); 453 currentCommand.nextParameter(); 454 setMode(Mode.SELECTION); 455 } 456 } else { 457 runTool(); 458 } 459 } else { 460 Main.info("Invalid argument"); 461 endInput(); 462 } 463 } 464 465 private void parseSelection(Collection<OsmPrimitive> selection) { 466 boolean ok = false; 467 for (OsmPrimitive obj : selection) { 468 ok = currentCommand.loadObject(obj); 469 if (!ok) 470 break; 471 } 472 if (ok) { 473 currentCommand.nextParameter(); 474 } else { 475 currentCommand.resetLoading(); 476 } 477 } 478 479 private class ToolProcess { 480 public Process process; 481 public volatile boolean running; 482 } 483 484 // Thanks to Upliner 485 public void runTool() { 486 setMode(Mode.PROCESSING); 487 String commandToRun = currentCommand.run; 488 final boolean tracks = currentCommand.tracks; 489 final ArrayList<Parameter> parameters = currentCommand.parameters; 490 491 for (Parameter parameter : currentCommand.parameters) { 492 commandToRun = commandToRun.replace("{" + parameter.name + "}", parameter.getValue()); 493 } 494 for (Parameter parameter : currentCommand.optParameters) { 495 commandToRun = commandToRun.replace("{" + parameter.name + "}", parameter.getValue()); 496 } 497 String[] listToRun = commandToRun.split(" "); 498 499 // create the process 500 final Object syncObj = new Object(); 501 502 ProcessBuilder builder; 503 builder = new ProcessBuilder(listToRun); 504 builder.directory(new File(getPluginDir())); 505 506 final StringBuilder debugstr = new StringBuilder(); 507 508 // debug: print resulting cmdline 509 for (String s : builder.command()) 510 debugstr.append(s + " "); 511 debugstr.append("\n"); 512 Main.info(debugstr.toString()); 513 514 final ToolProcess tp = new ToolProcess(); 515 try { 516 tp.process = builder.start(); 517 } catch (final IOException e) { 518 synchronized (debugstr) { 519 Main.error( 520 tr("Error executing the script: ") + 521 debugstr.toString() + e.getMessage() + "\n" + e.getStackTrace()); 522 } 523 return; 524 } 525 tp.running = true; 526 527 // redirect child process's stderr to JOSM stderr 528 new Thread(new Runnable() { 529 @Override 530 public void run() { 531 try { 532 byte[] buffer = new byte[1024]; 533 InputStream errStream = tp.process.getErrorStream(); 534 int len; 535 while ((len = errStream.read(buffer)) > 0) { 536 synchronized (debugstr) { 537 debugstr.append(new String(buffer, 0, len)); 538 } 539 System.err.write(buffer, 0, len); 540 } 541 } catch (IOException e) { 542 } 543 } 544 }).start(); 545 546 // Write stdin stream 547 Thread osmWriteThread = new Thread(new Runnable() { 548 @Override 549 public void run() { 550 BBox bbox = null; 551 final OutputStream outputStream = tp.process.getOutputStream(); 552 PrintWriter printWriter = null; 553 try { 554 printWriter = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)); 555 } catch (Exception e) { 556 Main.error(e); 557 } 558 final OsmWriter osmWriter = OsmWriterFactory.createOsmWriter(printWriter, true, null); 559 Collection<OsmPrimitive> refObjects = currentCommand.getDepsObjects(); 560 Collection<OsmPrimitive> pObjects; 561 osmWriter.header(); 562 Collection<OsmPrimitive> contents = new ArrayList<>(); 563 for (OsmPrimitive primitive : refObjects) { 564 contents.add(primitive); 565 if (bbox == null) 566 bbox = new BBox(primitive.getBBox()); 567 else 568 bbox.addPrimitive(primitive, 0.0); 569 } 570 osmWriter.writeNodes(new SubclassFilteredCollection<OsmPrimitive, Node>(contents, OsmPrimitive.nodePredicate)); 571 osmWriter.writeWays(new SubclassFilteredCollection<OsmPrimitive, Way>(contents, OsmPrimitive.wayPredicate)); 572 osmWriter.writeRelations(new SubclassFilteredCollection<OsmPrimitive, Relation>(contents, OsmPrimitive.relationPredicate)); 573 osmWriter.footer(); 574 osmWriter.flush(); 575 576 for (Parameter parameter : parameters) { 577 if (!parameter.isOsm()) 578 continue; 579 contents = new ArrayList<>(); 580 osmWriter.header(); 581 pObjects = parameter.getParameterObjects(); 582 for (OsmPrimitive primitive : pObjects) { 583 contents.add(primitive); 584 if (bbox == null) 585 bbox = new BBox(primitive.getBBox()); 586 else 587 bbox.addPrimitive(primitive, 0.0); 588 } 589 osmWriter.writeNodes(new SubclassFilteredCollection<OsmPrimitive, Node>(contents, OsmPrimitive.nodePredicate)); 590 osmWriter.writeWays(new SubclassFilteredCollection<OsmPrimitive, Way>(contents, OsmPrimitive.wayPredicate)); 591 osmWriter.writeRelations(new SubclassFilteredCollection<OsmPrimitive, Relation>(contents, OsmPrimitive.relationPredicate)); 592 osmWriter.footer(); 593 osmWriter.flush(); 594 } 595 596 if (tracks) { 597 try (GpxWriter gpxWriter = new GpxWriter(printWriter)) { 598 GpxFilter gpxFilter = new GpxFilter(); 599 gpxFilter.initBboxFilter(bbox); 600 List<GpxLayer> gpxLayers = Main.map.mapView.getLayersOfType(GpxLayer.class); 601 for (GpxLayer gpxLayer : gpxLayers) { 602 gpxFilter.addGpxData(gpxLayer.data); 603 } 604 gpxWriter.write(gpxFilter.getGpxData()); 605 } catch (IOException e) { 606 Main.warn(e); 607 } 608 } 609 Utils.close(osmWriter); 610 synchronized (syncObj) { 611 if (currentCommand.asynchronous) { 612 tp.running = false; 613 syncObj.notifyAll(); 614 } 615 } 616 } 617 }); 618 619 // Read stdout stream 620 final DataSet currentDataSet = Main.main.getCurrentDataSet(); 621 final CommandLine that = this; 622 Thread osmParseThread = new Thread(new Runnable() { 623 @Override 624 public void run() { 625 try { 626 final OsmToCmd osmToCmd = new OsmToCmd(that, currentDataSet); 627 String commandName = currentCommand.name; 628 final InputStream inputStream = tp.process.getInputStream(); 629 osmToCmd.parseStream(inputStream); 630 final List<org.openstreetmap.josm.command.Command> cmdlist = osmToCmd.getCommandList(); 631 if (!cmdlist.isEmpty()) { 632 final SequenceCommand cmd = new SequenceCommand(commandName, cmdlist); 633 SwingUtilities.invokeLater(new Runnable() { 634 @Override 635 public void run() { 636 Main.main.undoRedo.add(cmd); 637 } 638 }); 639 } 640 } catch (Exception e) { 641 } 642 finally { 643 synchronized (syncObj) { 644 tp.running = false; 645 syncObj.notifyAll(); 646 } 647 } 648 } 649 650 }); 651 652 osmParseThread.start(); 653 osmWriteThread.start(); 654 655 synchronized (syncObj) { 656 try { 657 syncObj.wait(Main.pref.getInteger("commandline.timeout", 20000)); 658 } catch (InterruptedException e) { 659 } 660 } 661 if (tp.running) { 662 new Thread(new PleaseWaitRunnable(currentCommand.name) { 663 @Override 664 protected void realRun() { 665 try { 666 progressMonitor.indeterminateSubTask(null); 667 synchronized (syncObj) { 668 if (tp.running) 669 syncObj.wait(); 670 } 671 } catch (InterruptedException e) { 672 } 673 } 674 675 @Override 676 protected void cancel() { 677 synchronized (syncObj) { 678 tp.running = false; 679 tp.process.destroy(); 680 syncObj.notifyAll(); 681 endInput(); 682 } 683 } 684 685 @Override 686 protected void finish() { 687 } 688 }).start(); 689 } 690 endInput(); 691 } 80 protected JTextField textField; 81 protected JTextField historyField; 82 private String prefix; 83 private Mode mode; 84 private ArrayList<Command> commands; 85 private JMenu commandMenu; 86 protected Command currentCommand; 87 protected String commandSymbol; 88 protected History history; 89 protected MapFrame currentMapFrame; 90 protected MapMode previousMode; 91 92 static final String pluginDir = Main.pref.getPluginsDirectory().getAbsolutePath() + "/CommandLine/"; 93 94 @SuppressWarnings("serial") 95 public CommandLine(PluginInformation info) { 96 super(info); 97 commandSymbol = ": "; 98 history = new History(100); 99 historyField = new DisableShortcutsOnFocusGainedTextField(); 100 textField = new DisableShortcutsOnFocusGainedTextField() { 101 @Override 102 protected void processKeyEvent(KeyEvent e) { 103 if (e.getID() == KeyEvent.KEY_PRESSED) { 104 int code = e.getKeyCode(); 105 if (code == KeyEvent.VK_ENTER) { 106 String commandText = textField.getText().substring(prefix.length()); 107 switch (mode) { 108 case IDLE: 109 if (commandText.isEmpty()) { 110 commandText = history.getLastItem(); 111 } else { 112 history.addItem(commandText); 113 } 114 Command command = findCommand(commandText, true); 115 if (command != null) { 116 startCommand(command); 117 } else { 118 setMode(Mode.IDLE); 119 } 120 break; 121 case SELECTION: 122 if (currentMapFrame.mapMode instanceof WayAction || currentMapFrame.mapMode instanceof NodeAction || currentMapFrame.mapMode instanceof RelationAction || currentMapFrame.mapMode instanceof AnyAction) { 123 Collection<OsmPrimitive> selected = Main.main.getCurrentDataSet().getSelected(); 124 if (selected.size() > 0) 125 loadParameter(selected, true); 126 } 127 else { 128 loadParameter(commandText, currentCommand.parameters.get(currentCommand.currentParameterNum).maxInstances == 1); 129 } 130 break; 131 case ADJUSTMENT: 132 break; 133 } 134 e.consume(); 135 } 136 else if (code == KeyEvent.VK_UP) { 137 textField.setText(prefix + history.getPrevItem()); 138 e.consume(); 139 } 140 else if (code == KeyEvent.VK_DOWN) { 141 textField.setText(prefix + history.getNextItem()); 142 e.consume(); 143 } 144 else if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) { 145 if (textField.getCaretPosition() <= prefix.length()) 146 e.consume(); 147 } 148 else if (code == KeyEvent.VK_HOME) { 149 setCaretPosition(prefix.length()); 150 e.consume(); 151 } 152 else if (code == KeyEvent.VK_ESCAPE) { 153 if (textField.getText().length() == prefix.length() && mode == Mode.IDLE) 154 deactivate(); 155 else 156 endInput(); 157 e.consume(); 158 } 159 else if (code == KeyEvent.VK_DELETE || code == KeyEvent.VK_RIGHT || code == KeyEvent.VK_END) { 160 } 161 else { 162 e.consume(); 163 } 164 if (textField.getCaretPosition() < prefix.length() || (textField.getSelectionStart() < prefix.length() && textField.getSelectionStart() > 0) ) 165 e.consume(); 166 } 167 if (e.getID() == KeyEvent.KEY_TYPED) 168 if (textField.getCaretPosition() < prefix.length() || (textField.getSelectionStart() < prefix.length() && textField.getSelectionStart() > 0) ) 169 e.consume(); 170 super.processKeyEvent(e); 171 if (textField.getText().length() < prefix.length()) { // Safe 172 setMode(mode); 173 } 174 if (e.getID() == KeyEvent.KEY_TYPED) { 175 if (e.getKeyChar() > 'A' && e.getKeyChar() < 'z') { 176 Command command = findCommand(textField.getText().substring(prefix.length()), false); 177 if (command != null) { 178 int currentPos = textField.getSelectionStart() == 0 ? textField.getCaretPosition() : textField.getSelectionStart(); 179 textField.setText(prefix + command.name); 180 textField.setCaretPosition(currentPos); 181 textField.select(currentPos, prefix.length() + command.name.length()); 182 } 183 } 184 } 185 } 186 @Override 187 protected void processMouseEvent(MouseEvent e) { 188 super.processMouseEvent(e); 189 if (e.getButton() == MouseEvent.BUTTON1 && e.getID() == MouseEvent.MOUSE_RELEASED) { 190 if (textField.getSelectionStart() > 0 && textField.getSelectionStart() < prefix.length()) 191 textField.setSelectionStart(prefix.length()); 192 else if (textField.getCaretPosition() < prefix.length()) 193 textField.setCaretPosition(prefix.length()); 194 } 195 } 196 }; 197 198 if (Main.main.menu != null) { 199 commandMenu = Main.main.menu.addMenu("Commands", tr("Commands"), KeyEvent.VK_O, Main.main.menu.getDefaultMenuPos(), ht("/Plugin/CommandLine")); 200 MainMenu.add(commandMenu, new CommandLineAction(this)); 201 } 202 loadCommands(); 203 setMode(Mode.IDLE); 204 } 205 206 public void startCommand(String commandName) { 207 Command command = findCommand(commandName, true); 208 if (command != null) { 209 startCommand(command); 210 } 211 } 212 213 protected void startCommand(Command command) { 214 if (Main.map == null) 215 return; 216 DataSet ds = Main.main.getCurrentDataSet(); 217 if (ds == null) 218 return; 219 currentCommand = command; 220 currentCommand.resetLoading(); 221 parseSelection(ds.getSelected()); 222 if (!(Main.map.mapMode instanceof AnyAction || Main.map.mapMode instanceof DummyAction || Main.map.mapMode instanceof LengthAction || Main.map.mapMode instanceof NodeAction || Main.map.mapMode instanceof PointAction || Main.map.mapMode instanceof RelationAction || Main.map.mapMode instanceof WayAction)) { 223 previousMode = Main.map.mapMode; 224 } 225 if (currentCommand.currentParameterNum < currentCommand.parameters.size()) 226 setMode(Mode.SELECTION); 227 else 228 runTool(); 229 } 230 231 @Override 232 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 233 currentMapFrame = newFrame; 234 if (oldFrame == null && newFrame != null) { 235 JToolBar tb = new JToolBar(); 236 tb.setLayout(new BorderLayout()); 237 tb.setFloatable(false); 238 tb.setOrientation(JToolBar.HORIZONTAL); 239 tb.add(historyField, BorderLayout.NORTH); 240 tb.add(textField, BorderLayout.SOUTH); 241 currentMapFrame.add(tb, BorderLayout.NORTH); 242 printHistory("Loaded CommandLine, version " + getPluginInformation().version); 243 } 244 } 245 246 protected void printHistory(final String text) { 247 SwingUtilities.invokeLater(new Runnable() { 248 @Override 249 public void run() { 250 historyField.setText(text); 251 } 252 }); 253 } 254 255 private void loadCommands() { 256 commands = (new Loader(getPluginDir())).load(); 257 if (commands.isEmpty()) { 258 if (!GraphicsEnvironment.isHeadless() && JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Main.parent, 259 tr("No command has been found. Would you like to download and install default commands now?"), 260 tr("No command found"), JOptionPane.YES_NO_CANCEL_OPTION)) { 261 try { 262 downloadAndInstallDefaultCommands(); 263 commands = (new Loader(getPluginDir())).load(); 264 JOptionPane.showMessageDialog(Main.parent, tr("Default commands have been successfully installed"), 265 tr("Success"), JOptionPane.INFORMATION_MESSAGE); 266 } catch (IOException e) { 267 Main.warn(e); 268 JOptionPane.showMessageDialog(Main.parent, 269 tr("Failed to download and install default commands.\n\nError: {0}", e.getMessage()), 270 tr("Warning"), JOptionPane.WARNING_MESSAGE); 271 } 272 } 273 } 274 for (Command command : commands) { 275 commandMenu.add(new CommandAction(command, this)); 276 } 277 } 278 279 private void downloadAndInstallDefaultCommands() throws IOException { 280 String url = Main.pref.get("commandline.default.commands.url", 281 "https://github.com/Foxhind/JOSM-CommandLine-commands/archive/master.zip"); 282 try (ZipInputStream zis = new ZipInputStream(HttpClient.create(new URL(url)).connect().getContent(), StandardCharsets.UTF_8)) { 283 File dir = new File(getPluginDir()); 284 if (!dir.exists()) { 285 dir.mkdirs(); 286 } 287 ZipEntry entry = null; 288 while ( (entry = zis.getNextEntry()) != null ) { 289 if (!entry.isDirectory()) { 290 String name = entry.getName(); 291 if (name.contains("/")) { 292 name = name.substring(name.lastIndexOf("/")); 293 } 294 File file = new File(dir + File.separator + name); 295 Main.info("Installing command file: "+file); 296 if (!file.createNewFile()) { 297 throw new IOException("Could not create file: " + file.getAbsolutePath()); 298 } 299 // Write file 300 Files.copy(zis, file.toPath(), StandardCopyOption.REPLACE_EXISTING); 301 // Set last modification date 302 long time = entry.getTime(); 303 if (time > -1) { 304 file.setLastModified(time); 305 } 306 } 307 } 308 } 309 } 310 311 private Command findCommand(String text, boolean strict) { 312 for (int i = 0; i < commands.size(); i++) { 313 if (strict) { 314 if ( commands.get(i).name.equalsIgnoreCase(text) ) { 315 return commands.get(i); 316 } 317 } else if ( commands.get(i).name.toLowerCase().startsWith( text.toLowerCase() ) && text.length() > 1 ) { 318 return commands.get(i); 319 } 320 } 321 return null; 322 } 323 324 protected void setMode(Mode targetMode) { 325 DataSet currentDataSet = Main.main.getCurrentDataSet(); 326 if (currentDataSet != null) { 327 currentDataSet.clearSelection(); 328 Main.map.mapView.repaint(); 329 } 330 if (targetMode == Mode.IDLE) { 331 mode = Mode.IDLE; 332 currentCommand = null; 333 prefix = tr("Command") + commandSymbol; 334 textField.setText(prefix); 335 } 336 else if (targetMode == Mode.SELECTION) { 337 mode = Mode.SELECTION; 338 Parameter currentParameter = currentCommand.parameters.get(currentCommand.currentParameterNum); 339 prefix = tr(currentParameter.description == null ? currentParameter.name : currentParameter.description); 340 if (currentParameter.getRawValue() instanceof Relay) 341 prefix = prefix + " (" + ((Relay)(currentParameter.getRawValue())).getOptionsString() + ")"; 342 prefix += commandSymbol; 343 String value = currentParameter.getValue(); 344 textField.setText(prefix + value); 345 Type currentType = currentParameter.type; 346 MapMode action = null; 347 switch (currentType) { 348 case POINT: 349 action = new PointAction(currentMapFrame, this); 350 break; 351 case WAY: 352 action = new WayAction(currentMapFrame, this); 353 break; 354 case NODE: 355 action = new NodeAction(currentMapFrame, this); 356 break; 357 case RELATION: 358 action = new RelationAction(currentMapFrame, this); 359 break; 360 case ANY: 361 action = new AnyAction(currentMapFrame, this); 362 break; 363 case LENGTH: 364 action = new LengthAction(currentMapFrame, this); 365 break; 366 case USERNAME: 367 loadParameter(Main.pref.get("osm-server.username", null), true); 368 action = new DummyAction(currentMapFrame, this); 369 break; 370 case IMAGERYURL: 371 Layer layer = Main.map.mapView.getActiveLayer(); 372 if (layer != null) { 373 if (!(layer instanceof ImageryLayer)) { 374 List<ImageryLayer> imageryLayers = Main.map.mapView.getLayersOfType(ImageryLayer.class); 375 if (imageryLayers.size() == 1) { 376 layer = imageryLayers.get(0); 377 } 378 else { 379 endInput(); 380 return; 381 } 382 } 383 } 384 ImageryInfo info = ((ImageryLayer)layer).getInfo(); 385 String url = info.getUrl(); 386 String itype = info.getImageryType().getTypeString(); 387 loadParameter((url.equals("") ? itype : url), true); 388 action = new DummyAction(currentMapFrame, this); 389 break; 390 case IMAGERYOFFSET: 391 Layer olayer = Main.map.mapView.getActiveLayer(); 392 if (olayer != null) { 393 if (!(olayer instanceof ImageryLayer)) { 394 List<ImageryLayer> imageryLayers = Main.map.mapView.getLayersOfType(ImageryLayer.class); 395 if (imageryLayers.size() == 1) { 396 olayer = imageryLayers.get(0); 397 } 398 else { 399 endInput(); 400 return; 401 } 402 } 403 } 404 loadParameter((String.valueOf(((ImageryLayer)olayer).getDx()) + "," + String.valueOf(((ImageryLayer)olayer).getDy())), true); 405 action = new DummyAction(currentMapFrame, this); 406 break; 407 default: 408 action = new DummyAction(currentMapFrame, this); 409 break; 410 } 411 currentMapFrame.selectMapMode(action); 412 activate(); 413 textField.select(prefix.length(), textField.getText().length()); 414 } 415 else if (targetMode == Mode.PROCESSING) { 416 mode = Mode.PROCESSING; 417 prefix = tr("Processing..."); 418 textField.setText(prefix); 419 Main.map.mapView.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 420 } 421 } 422 423 public void activate() { 424 textField.requestFocus(); 425 textField.setCaretPosition(textField.getText().length()); 426 } 427 428 public void deactivate() { 429 Main.map.mapView.requestFocus(); 430 } 431 432 public void abortInput() { 433 printHistory(tr("Aborted") + "."); 434 endInput(); 435 } 436 437 public void endInput() { 438 setMode(Mode.IDLE); 439 Main.map.selectMapMode(previousMode); 440 Main.map.mapView.repaint(); 441 } 442 443 public void loadParameter(Object obj, boolean next) { 444 if (currentCommand.loadObject(obj)) { 445 if (currentCommand.hasNextParameter()) { 446 if (next) { 447 Parameter currentParameter = currentCommand.parameters.get(currentCommand.currentParameterNum); 448 String prefix = tr(currentParameter.description == null ? currentParameter.name : currentParameter.description); 449 prefix += commandSymbol; 450 String value = currentParameter.getValue(); 451 printHistory(prefix + value); 452 currentCommand.nextParameter(); 453 setMode(Mode.SELECTION); 454 } 455 } else { 456 runTool(); 457 } 458 } else { 459 Main.info("Invalid argument"); 460 endInput(); 461 } 462 } 463 464 private void parseSelection(Collection<OsmPrimitive> selection) { 465 boolean ok = false; 466 for (OsmPrimitive obj : selection) { 467 ok = currentCommand.loadObject(obj); 468 if (!ok) 469 break; 470 } 471 if (ok) { 472 currentCommand.nextParameter(); 473 } else { 474 currentCommand.resetLoading(); 475 } 476 } 477 478 private class ToolProcess { 479 public Process process; 480 public volatile boolean running; 481 } 482 483 // Thanks to Upliner 484 public void runTool() { 485 setMode(Mode.PROCESSING); 486 String commandToRun = currentCommand.run; 487 final boolean tracks = currentCommand.tracks; 488 final ArrayList<Parameter> parameters = currentCommand.parameters; 489 490 for (Parameter parameter : currentCommand.parameters) { 491 commandToRun = commandToRun.replace("{" + parameter.name + "}", parameter.getValue()); 492 } 493 for (Parameter parameter : currentCommand.optParameters) { 494 commandToRun = commandToRun.replace("{" + parameter.name + "}", parameter.getValue()); 495 } 496 String[] listToRun = commandToRun.split(" "); 497 498 // create the process 499 final Object syncObj = new Object(); 500 501 ProcessBuilder builder; 502 builder = new ProcessBuilder(listToRun); 503 builder.directory(new File(getPluginDir())); 504 505 final StringBuilder debugstr = new StringBuilder(); 506 507 // debug: print resulting cmdline 508 for (String s : builder.command()) 509 debugstr.append(s + " "); 510 debugstr.append("\n"); 511 Main.info(debugstr.toString()); 512 513 final ToolProcess tp = new ToolProcess(); 514 try { 515 tp.process = builder.start(); 516 } catch (final IOException e) { 517 synchronized (debugstr) { 518 Main.error( 519 tr("Error executing the script: ") + 520 debugstr.toString() + e.getMessage() + "\n" + e.getStackTrace()); 521 } 522 return; 523 } 524 tp.running = true; 525 526 // redirect child process's stderr to JOSM stderr 527 new Thread(new Runnable() { 528 @Override 529 public void run() { 530 try { 531 byte[] buffer = new byte[1024]; 532 InputStream errStream = tp.process.getErrorStream(); 533 int len; 534 while ((len = errStream.read(buffer)) > 0) { 535 synchronized (debugstr) { 536 debugstr.append(new String(buffer, 0, len)); 537 } 538 System.err.write(buffer, 0, len); 539 } 540 } catch (IOException e) { 541 } 542 } 543 }).start(); 544 545 // Write stdin stream 546 Thread osmWriteThread = new Thread(new Runnable() { 547 @Override 548 public void run() { 549 BBox bbox = null; 550 final OutputStream outputStream = tp.process.getOutputStream(); 551 PrintWriter printWriter = null; 552 try { 553 printWriter = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)); 554 } catch (Exception e) { 555 Main.error(e); 556 } 557 final OsmWriter osmWriter = OsmWriterFactory.createOsmWriter(printWriter, true, null); 558 Collection<OsmPrimitive> refObjects = currentCommand.getDepsObjects(); 559 Collection<OsmPrimitive> pObjects; 560 osmWriter.header(); 561 Collection<OsmPrimitive> contents = new ArrayList<>(); 562 for (OsmPrimitive primitive : refObjects) { 563 contents.add(primitive); 564 if (bbox == null) 565 bbox = new BBox(primitive.getBBox()); 566 else 567 bbox.addPrimitive(primitive, 0.0); 568 } 569 osmWriter.writeNodes(new SubclassFilteredCollection<OsmPrimitive, Node>(contents, OsmPrimitive.nodePredicate)); 570 osmWriter.writeWays(new SubclassFilteredCollection<OsmPrimitive, Way>(contents, OsmPrimitive.wayPredicate)); 571 osmWriter.writeRelations(new SubclassFilteredCollection<OsmPrimitive, Relation>(contents, OsmPrimitive.relationPredicate)); 572 osmWriter.footer(); 573 osmWriter.flush(); 574 575 for (Parameter parameter : parameters) { 576 if (!parameter.isOsm()) 577 continue; 578 contents = new ArrayList<>(); 579 osmWriter.header(); 580 pObjects = parameter.getParameterObjects(); 581 for (OsmPrimitive primitive : pObjects) { 582 contents.add(primitive); 583 if (bbox == null) 584 bbox = new BBox(primitive.getBBox()); 585 else 586 bbox.addPrimitive(primitive, 0.0); 587 } 588 osmWriter.writeNodes(new SubclassFilteredCollection<OsmPrimitive, Node>(contents, OsmPrimitive.nodePredicate)); 589 osmWriter.writeWays(new SubclassFilteredCollection<OsmPrimitive, Way>(contents, OsmPrimitive.wayPredicate)); 590 osmWriter.writeRelations(new SubclassFilteredCollection<OsmPrimitive, Relation>(contents, OsmPrimitive.relationPredicate)); 591 osmWriter.footer(); 592 osmWriter.flush(); 593 } 594 595 if (tracks) { 596 try (GpxWriter gpxWriter = new GpxWriter(printWriter)) { 597 GpxFilter gpxFilter = new GpxFilter(); 598 gpxFilter.initBboxFilter(bbox); 599 List<GpxLayer> gpxLayers = Main.map.mapView.getLayersOfType(GpxLayer.class); 600 for (GpxLayer gpxLayer : gpxLayers) { 601 gpxFilter.addGpxData(gpxLayer.data); 602 } 603 gpxWriter.write(gpxFilter.getGpxData()); 604 } catch (IOException e) { 605 Main.warn(e); 606 } 607 } 608 Utils.close(osmWriter); 609 synchronized (syncObj) { 610 if (currentCommand.asynchronous) { 611 tp.running = false; 612 syncObj.notifyAll(); 613 } 614 } 615 } 616 }); 617 618 // Read stdout stream 619 final DataSet currentDataSet = Main.main.getCurrentDataSet(); 620 final CommandLine that = this; 621 Thread osmParseThread = new Thread(new Runnable() { 622 @Override 623 public void run() { 624 try { 625 final OsmToCmd osmToCmd = new OsmToCmd(that, currentDataSet); 626 String commandName = currentCommand.name; 627 final InputStream inputStream = tp.process.getInputStream(); 628 osmToCmd.parseStream(inputStream); 629 final List<org.openstreetmap.josm.command.Command> cmdlist = osmToCmd.getCommandList(); 630 if (!cmdlist.isEmpty()) { 631 final SequenceCommand cmd = new SequenceCommand(commandName, cmdlist); 632 SwingUtilities.invokeLater(new Runnable() { 633 @Override 634 public void run() { 635 Main.main.undoRedo.add(cmd); 636 } 637 }); 638 } 639 } catch (Exception e) { 640 } 641 finally { 642 synchronized (syncObj) { 643 tp.running = false; 644 syncObj.notifyAll(); 645 } 646 } 647 } 648 649 }); 650 651 osmParseThread.start(); 652 osmWriteThread.start(); 653 654 synchronized (syncObj) { 655 try { 656 syncObj.wait(Main.pref.getInteger("commandline.timeout", 20000)); 657 } catch (InterruptedException e) { 658 } 659 } 660 if (tp.running) { 661 new Thread(new PleaseWaitRunnable(currentCommand.name) { 662 @Override 663 protected void realRun() { 664 try { 665 progressMonitor.indeterminateSubTask(null); 666 synchronized (syncObj) { 667 if (tp.running) 668 syncObj.wait(); 669 } 670 } catch (InterruptedException e) { 671 } 672 } 673 674 @Override 675 protected void cancel() { 676 synchronized (syncObj) { 677 tp.running = false; 678 tp.process.destroy(); 679 syncObj.notifyAll(); 680 endInput(); 681 } 682 } 683 684 @Override 685 protected void finish() { 686 } 687 }).start(); 688 } 689 endInput(); 690 } 692 691 }
Note:
See TracChangeset
for help on using the changeset viewer.