/** Construct the JobQueue. This is private; use getJobQueue() to get the job queue instance. */
  private JobQueue() {
    // determine which compiler we should be using
    String compilertype = Config.getPropString("bluej.compiler.type");

    // even though it is specified to use internal, the preferred compiler for a
    // system running Java 6 or greater is the JavaCompiler API
    if (compilertype.equals("internal")) {
      if (Config.isJava16()) {
        try {
          Class<?> c = Class.forName("bluej.compiler.CompilerAPICompiler");
          compiler = (Compiler) c.newInstance();
        } catch (Throwable e) {
          Debug.message(
              "Could not instantiate the compiler API compiler implementation; defaulting to old compiler");
          compiler = new JavacCompilerInternal();
        }
      } else {
        compiler = new JavacCompilerInternal();
      }
    } else if (compilertype.equals("javac")) {
      compiler =
          new JavacCompiler(Config.getJDKExecutablePath("bluej.compiler.executable", "javac"));
    } else {
      Debug.message(Config.getString("compiler.invalidcompiler"));
    }

    thread = new CompilerThread();

    // Lower priority to improve GUI response time during compilation
    int priority = Thread.currentThread().getPriority() - 1;
    priority = Math.max(priority, Thread.MIN_PRIORITY);
    thread.setPriority(priority);

    thread.start();
  }
Exemple #2
0
  /** Create the terminal's menubar, all menus and items. */
  private JMenuBar makeMenuBar() {
    JMenuBar menubar = new JMenuBar();
    JMenu menu = new JMenu(Config.getString("terminal.options"));
    JMenuItem item;
    item = menu.add(new ClearAction());
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_K, SHORTCUT_MASK));
    item = menu.add(getCopyAction());
    item.setText(Config.getString("terminal.copy"));
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_MASK));
    item = menu.add(new SaveAction());
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_MASK));
    menu.add(new PrintAction());
    menu.add(new JSeparator());

    autoClear = new JCheckBoxMenuItem(new AutoClearAction());
    autoClear.setSelected(clearOnMethodCall);
    menu.add(autoClear);

    recordCalls = new JCheckBoxMenuItem(new RecordCallAction());
    recordCalls.setSelected(recordMethodCalls);
    menu.add(recordCalls);

    unlimitedBuffering = new JCheckBoxMenuItem(new BufferAction());
    unlimitedBuffering.setSelected(unlimitedBufferingCall);
    menu.add(unlimitedBuffering);

    menu.add(new JSeparator());
    item = menu.add(new CloseAction());
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, SHORTCUT_MASK));

    menubar.add(menu);
    return menubar;
  }
Exemple #3
0
 /** Initialise the terminal; create the UI. */
 private synchronized void initialise() {
   if (!initialised) {
     buffer = new InputBuffer(256);
     int width = Config.isGreenfoot() ? 80 : Config.getPropInteger("bluej.terminal.width", 80);
     int height = Config.isGreenfoot() ? 10 : Config.getPropInteger("bluej.terminal.height", 22);
     makeWindow(width, height);
     initialised = true;
     text.setUnlimitedBuffering(unlimitedBufferingCall);
   }
 }
Exemple #4
0
  /**
   * Returns the language-dependent label with the given key. The search order is to look first in
   * the extension's <code>label</code> files and if the requested label is not found in the BlueJ
   * system <code>label</code> files. Extensions' labels are stored in a Property format and must be
   * jarred together with the extension. The path searched is equivalent to the bluej/lib/[language]
   * style used for the BlueJ system labels. E.g. to create a set of labels which can be used by
   * English, Italian and German users of an extension, the following files would need to be present
   * in the extension's Jar file:
   *
   * <pre>
   * lib/english/label
   * lib/italian/label
   * lib/german/label
   * </pre>
   *
   * The files named <code>label</code> would contain the actual label key/value pairs.
   *
   * @param key Description of the Parameter
   * @return The label value
   */
  public String getLabel(String key) {
    if (!myWrapper.isValid()) throw new ExtensionUnloadedException();

    // If there are no label for this extension I can only return the system ones.
    if (localLabels == null) return Config.getString(key, key);

    // In theory there are label for this extension let me try to get them
    String aLabel = localLabels.getProperty(key, null);

    // Found what I wanted, job done.
    if (aLabel != null) return aLabel;

    // ok, the only hope is to get it from the system
    return Config.getString(key, key);
  }
  /**
   * Generates a source code skeleton for this class.
   *
   * @param template the name of the particular class template (just the base name without path and
   *     suffix)
   * @param pkg the package that the class target resides in
   * @param name the name of the class
   * @param sourceFile the name of the source file to be generated
   */
  public boolean generateSkeleton(String template, Package pkg, String name, String sourceFile) {
    Hashtable<String, String> translations = new Hashtable<String, String>();
    translations.put("CLASSNAME", name);

    if (pkg.isUnnamedPackage()) {
      translations.put("PKGLINE", "");
    } else {
      translations.put(
          "PKGLINE", "package " + pkg.getQualifiedName() + ";" + Config.nl + Config.nl);
    }

    try {
      // Check for existing file. Normally this won't happen (the check for duplicate
      // target occurs prior to this) but on Windows filenames are case insensitive.
      File dest = new File(sourceFile);
      if (dest.exists()) {
        pkg.showError("duplicate-name");
        return false;
      }
      BlueJFileReader.translateFile(
          Config.getClassTemplateFile(template),
          new File(sourceFile),
          translations,
          Charset.forName("UTF-8"),
          pkg.getProject().getProjectCharset());
      return true;
    } catch (IOException e) {
      pkg.showError("skeleton-error");
      Debug.reportError("The default skeleton for the class could not be generated");
      Debug.reportError("Exception: " + e);
      return false;
    }
  }
 /** Check whether the key used for multiple selections is down. */
 private boolean isMultiselectionKeyDown(MouseEvent evt) {
   if (Config.isMacOS()) {
     return evt.isShiftDown() || evt.isMetaDown();
   } else {
     return evt.isShiftDown() || evt.isControlDown();
   }
 }
Exemple #7
0
 public void print() {
   PrinterJob job = PrinterJob.getPrinterJob();
   int printFontSize = Config.getPropInteger("bluej.fontsize.printText", 10);
   Font font = new Font("Monospaced", Font.PLAIN, printFontSize);
   if (job.printDialog()) {
     TerminalPrinter.printTerminal(job, text, job.defaultPage(), font);
   }
 }
Exemple #8
0
 /*
  * Set the terminal font size to equal either the passed parameter, or the
  * editor font size if the passed parameter is too low. Place the updated
  * value into the configuration.
  */
 public static void setTerminalFontSize(int size) {
   if (size <= 6) {
     return;
   } else {
     terminalFontSize = size;
   }
   Config.putPropInteger(TERMINALFONTSIZEPROPNAME, terminalFontSize);
 }
  /**
   * Adds a job to the compile queue.
   *
   * @param sources The files to compile
   * @param observer Observer to be notified when compilation begins, errors/warnings, completes
   * @param classPath The classpath to use to locate objects/source code
   * @param destDir Destination for class files?
   * @param suppressUnchecked Suppress "unchecked" warning in java 1.5
   */
  public void addJob(
      File[] sources,
      CompileObserver observer,
      BPClassLoader bpClassLoader,
      File destDir,
      boolean suppressUnchecked) {
    List<String> options = new ArrayList<String>();
    if (bpClassLoader.loadsForJavaMEproject()) {
      String optionString = Config.getPropString(Compiler.JAVAME_COMPILER_OPTIONS, null);
      Compiler.tokenizeOptionString(options, optionString);
    }
    String optionString = Config.getPropString(Compiler.COMPILER_OPTIONS, null);
    Compiler.tokenizeOptionString(options, optionString);

    thread.addJob(
        new Job(sources, compiler, observer, bpClassLoader, destDir, suppressUnchecked, options));
  }
Exemple #10
0
  /** Create a second scrolled text area to the window, for error output. */
  private void createErrorPane() {
    errorText = new TermTextArea(Config.isGreenfoot() ? 20 : 5, 80, null, project, this, true);
    errorScrollPane = new JScrollPane(errorText);
    errorText.setFont(getTerminalFont());
    errorText.setEditable(false);
    errorText.setMargin(new Insets(6, 6, 6, 6));
    errorText.setUnlimitedBuffering(true);

    splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, errorScrollPane);
  }
Exemple #11
0
 /** Prepare the terminal for I/O. */
 private void prepare() {
   if (newMethodCall) { // prepare only once per method call
     showHide(true);
     newMethodCall = false;
   } else if (Config.isGreenfoot()) {
     // In greenfoot new output should always show the terminal
     if (!isVisible()) {
       showHide(true);
     }
   }
 }
 /**
  * Gets the button for the mouse event. This will also translate CTRL-clicks on mac into mouse
  * button three.
  */
 private int getButton(MouseEvent e) {
   int button = e.getButton();
   if (Config.isMacOS() && button == MouseEvent.BUTTON1) {
     // Simulate right click on Macs that use CTRL click for right
     // clicks. Would be nice if we could use isPopupTrigger instead, but
     // that only works for mouse pressed.
     if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == MouseEvent.CTRL_DOWN_MASK) {
       button = MouseEvent.BUTTON3;
     }
   }
   return button;
 }
  public EditorDividerPanel(NaviView naviview, boolean expanded) {
    super();
    // display consists of a label with an image
    nav = naviview;
    this.expanded = expanded;
    openNavArrow = Config.getImageAsIcon("image.editordivider.open");
    closeNavArrow = Config.getImageAsIcon("image.editordivider.close");

    setPreferredSize(new Dimension(closeNavArrow.getIconWidth() + 2, 0));
    setMaximumSize(new Dimension(closeNavArrow.getIconWidth() + 2, Integer.MAX_VALUE));

    setLayout(new DBoxLayout(DBox.X_AXIS, 0, 0));
    expandCollapseButton = new JLabel();
    expandCollapseButton.setName(EXPAND_COLLAPSE_NAVIVIEW);
    addMouseListener(this);
    add(expandCollapseButton, BorderLayout.CENTER);
    if (isExpanded()) expandCollapseButton.setIcon(closeNavArrow);
    else {
      nav.setVisible(false);
      expandCollapseButton.setIcon(openNavArrow);
    }
  }
Exemple #14
0
 /** Save the terminal text to file. */
 public void save() {
   initialise();
   String fileName =
       FileUtility.getFileName(
           this,
           Config.getString("terminal.save.title"),
           Config.getString("terminal.save.buttonText"),
           null,
           false);
   if (fileName != null) {
     File f = new File(fileName);
     if (f.exists()) {
       if (DialogManager.askQuestion(this, "error-file-exists") != 0) return;
     }
     try {
       FileWriter writer = new FileWriter(fileName);
       text.write(writer);
       writer.close();
     } catch (IOException ex) {
       DialogManager.showError(this, "error-save-file");
     }
   }
 }
 /**
  * Return the default background colour for targets that don't want to define their own colour.
  *
  * @param width Width of total area to paint
  * @param height Height of total area to paint
  */
 public Paint getBackgroundPaint(int width, int height) {
   if (defaultbg != null) {
     return defaultbg;
   } else {
     Paint result;
     if (!Config.isRaspberryPi()) {
       result =
           new GradientPaint(0, 0, new Color(246, 221, 192), 0, height, new Color(245, 204, 155));
     } else {
       // return the average colour.
       result = new Color(246, 233, 174);
     }
     return result;
   }
 }
/**
 * A role object to represent the behaviour of interfaces.
 *
 * @author Andrew Patterson
 * @version $Id: InterfaceClassRole.java 6215 2009-03-30 13:28:25Z polle $
 */
public class InterfaceClassRole extends ClassRole {
  public static final String INTERFACE_ROLE_NAME = "InterfaceTarget";
  private static final Color interfacebg = Config.getItemColour("colour.class.bg.interface");

  /** Create the interface class role. */
  public InterfaceClassRole() {}

  public String getRoleName() {
    return INTERFACE_ROLE_NAME;
  }

  public String getStereotypeLabel() {
    return "interface";
  }

  /** Return the intended background colour for this type of target. */
  public Color getBackgroundColour() {
    return interfacebg;
  }
}
Exemple #17
0
 public void actionPerformed(ActionEvent e) {
   clearOnMethodCall = autoClear.isSelected();
   Config.putPropBoolean(CLEARONMETHODCALLSPROPNAME, clearOnMethodCall);
 }
Exemple #18
0
  /**
   * Return a property associated with this extension from the standard BlueJ property repository.
   * You must use the setExtensionPropertyString to write any property that you want stored. You can
   * then come back and retrieve it using this function.
   *
   * @param property The name of the required global property.
   * @param def The default value to use if the property cannot be found.
   * @return the value of that property.
   */
  public String getExtensionPropertyString(String property, String def) {
    if (!myWrapper.isValid()) throw new ExtensionUnloadedException();

    String thisKey = myWrapper.getSettingsString(property);
    return Config.getPropString(thisKey, def);
  }
Exemple #19
0
  /**
   * Sets a property associated with this extension into the standard BlueJ property repository. The
   * property name does not need to be fully qualified since a prefix will be prepended to it.
   *
   * @param property The name of the required global property
   * @param value the required value of that property.
   */
  public void setExtensionPropertyString(String property, String value) {
    if (!myWrapper.isValid()) throw new ExtensionUnloadedException();

    String thisKey = myWrapper.getSettingsString(property);
    Config.putPropString(thisKey, value);
  }
Exemple #20
0
  /**
   * Returns the path of the user configuration directory. This can be used to locate user dependent
   * information. Having the directory you can then locate a file within it.
   *
   * @return The userConfigDir value
   */
  public File getUserConfigDir() {
    if (!myWrapper.isValid()) throw new ExtensionUnloadedException();

    return Config.getUserConfigDir();
  }
Exemple #21
0
  /**
   * Returns a property from BlueJ's properties, or the given default value if the property is not
   * currently set.
   *
   * @param property The name of the required global property
   * @param def The default value to use if the property cannot be found.
   * @return the value of the property.
   */
  public String getBlueJPropertyString(String property, String def) {
    if (!myWrapper.isValid()) throw new ExtensionUnloadedException();

    return Config.getPropString(property, def);
  }
/**
 * Syntax colouring for the codepad.
 *
 * @author Bruce Quig
 * @author Michael Kolling
 */
public class TextEvalSyntaxView extends BlueJSyntaxView {
  public static final short TAG_WIDTH = 14;
  protected static final int BREAKPOINT_OFFSET = TAG_WIDTH + 2;
  protected static final int LEFT_MARGIN = BREAKPOINT_OFFSET + 5;

  // Attributes for lines and document
  public static final String OUTPUT = "output";
  public static final String ERROR = "error";
  public static final String CONTINUE = "continue";
  public static final String OBJECT = "object-ref";

  static final Image promptImage = Config.getImageAsIcon("image.eval.prompt").getImage();
  static final Image continueImage = Config.getImageAsIcon("image.eval.continue").getImage();
  static final Image objectImage = Config.getImageAsIcon("image.eval.object").getImage();

  static final Color outputColor = new Color(0, 120, 0);
  static final Color errorColor = new Color(200, 0, 20);

  /**
   * Creates a new TextEvalSyntaxView for painting the specified element.
   *
   * @param elem The element
   */
  public TextEvalSyntaxView(Element elem) {
    super(elem, LEFT_MARGIN);
  }

  /** Draw a line for the text eval area. */
  public void paintTaggedLine(
      Segment lineText,
      int lineIndex,
      Graphics g,
      int x,
      int y,
      MoeSyntaxDocument document,
      Color def,
      Element line,
      TabExpander tx) {
    if (hasTag(line, OUTPUT)) {
      g.setColor(outputColor);
      Utilities.drawTabbedText(lineText, x, y, g, tx, 0);
    } else if (hasTag(line, ERROR)) {
      g.setColor(errorColor);
      Utilities.drawTabbedText(lineText, x, y, g, tx, 0);
    } else if (hasObject(line, OBJECT)) {
      g.drawImage(objectImage, x - 1 - LEFT_MARGIN, y + 3 - objectImage.getHeight(null), null);
      g.setColor(outputColor);
      Utilities.drawTabbedText(lineText, x, y, g, tx, 0);
    } else if (hasTag(line, CONTINUE)) {
      g.drawImage(continueImage, x - 1 - LEFT_MARGIN, y + 3 - continueImage.getHeight(null), null);
      paintSyntaxLine(lineText, lineIndex, x, y, g, document, def, tx);
    } else {
      g.drawImage(promptImage, x - 1 - LEFT_MARGIN, y + 3 - promptImage.getHeight(null), null);
      paintSyntaxLine(lineText, lineIndex, x, y, g, document, def, tx);
    }
  }

  /**
   * Check whether a given line is tagged with a given tag.
   *
   * @param line The line to check
   * @param tag The name of the tag
   * @return True, if the tag is set
   */
  protected final boolean hasObject(Element line, String tag) {
    return line.getAttributes().getAttribute(tag) != null;
  }

  /** redefined paint method to paint breakpoint area */
  public void paint(Graphics g, Shape allocation) {
    Rectangle bounds = allocation.getBounds();

    // paint the lines
    super.paint(g, allocation);

    // paint the tag separator line
    g.setColor(Color.lightGray);
    g.drawLine(bounds.x + TAG_WIDTH, 0, bounds.x + TAG_WIDTH, bounds.y + bounds.height);
  }
}
Exemple #23
0
  /**
   * Returns the path of the <code>&lt;BLUEJ_HOME&gt;/lib</code> system directory. This can be used
   * to locate systemwide configuration files. Having the directory you can then locate a file
   * within it.
   *
   * @return The systemLibDir value
   */
  public File getSystemLibDir() {
    if (!myWrapper.isValid()) throw new ExtensionUnloadedException();

    return Config.getBlueJLibDir();
  }
Exemple #24
0
 public NewClassAction(GreenfootFrame gfFrame) {
   super(Config.getString("new.class"));
   setEnabled(false);
   this.gfFrame = gfFrame;
 }
 public CommitCommentAction() {
   super("team.commit", true);
   putValue(SHORT_DESCRIPTION, Config.getString("tooltip.commit"));
 }
Exemple #26
0
/**
 * The Frame part of the Terminal window used for I/O when running programs under BlueJ.
 *
 * @author Michael Kolling
 * @author Philip Stevens
 */
@SuppressWarnings("serial")
public final class Terminal extends JFrame
    implements KeyListener, BlueJEventListener, DebuggerTerminal {
  private static final String WINDOWTITLE =
      Config.getApplicationName() + ": " + Config.getString("terminal.title");
  private static final int SHORTCUT_MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
  // private static final int ALT_SHORTCUT_MASK =
  //        SHORTCUT_MASK == Event.CTRL_MASK ? Event.CTRL_MASK : Event.META_MASK;

  private static final String TERMINALFONTPROPNAME = "bluej.terminal.font";
  private static final String TERMINALFONTSIZEPROPNAME = "bluej.editor.fontsize";

  private static final String RECORDMETHODCALLSPROPNAME = "bluej.terminal.recordcalls";
  private static final String CLEARONMETHODCALLSPROPNAME = "bluej.terminal.clearscreen";
  private static final String UNLIMITEDBUFFERINGCALLPROPNAME = "bluej.terminal.buffering";

  // initialise to config value or zero.
  private static int terminalFontSize =
      Config.getPropInteger(TERMINALFONTSIZEPROPNAME, PrefMgr.getEditorFontSize());

  private static boolean isMacOs = Config.isMacOS();

  // -- instance --

  private Project project;

  private TermTextArea text;
  private TermTextArea errorText;
  private JScrollPane errorScrollPane;
  private JScrollPane scrollPane;
  private JSplitPane splitPane;
  private boolean isActive = false;
  private static boolean recordMethodCalls = Config.getPropBoolean(RECORDMETHODCALLSPROPNAME);
  private static boolean clearOnMethodCall = Config.getPropBoolean(CLEARONMETHODCALLSPROPNAME);
  private static boolean unlimitedBufferingCall =
      Config.getPropBoolean(UNLIMITEDBUFFERINGCALLPROPNAME);
  private boolean newMethodCall = true;
  private boolean errorShown = false;
  private InputBuffer buffer;

  private JCheckBoxMenuItem autoClear;
  private JCheckBoxMenuItem recordCalls;
  private JCheckBoxMenuItem unlimitedBuffering;

  private Reader in = new TerminalReader();
  private Writer out = new TerminalWriter(false);
  private Writer err = new TerminalWriter(true);

  /** Used for lazy initialisation */
  private boolean initialised = false;

  /** Create a new terminal window with default specifications. */
  public Terminal(Project project) {
    super(WINDOWTITLE + " - " + project.getProjectName());
    this.project = project;
    BlueJEvent.addListener(this);
  }

  /** Get the terminal font */
  private static Font getTerminalFont() {
    // reload terminal fontsize from configurations.

    terminalFontSize = Config.getPropInteger(TERMINALFONTSIZEPROPNAME, PrefMgr.getEditorFontSize());
    return Config.getFont(TERMINALFONTPROPNAME, "Monospaced", terminalFontSize);
  }

  /*
   * Set the terminal font size to equal either the passed parameter, or the
   * editor font size if the passed parameter is too low. Place the updated
   * value into the configuration.
   */
  public static void setTerminalFontSize(int size) {
    if (size <= 6) {
      return;
    } else {
      terminalFontSize = size;
    }
    Config.putPropInteger(TERMINALFONTSIZEPROPNAME, terminalFontSize);
  }

  /** Initialise the terminal; create the UI. */
  private synchronized void initialise() {
    if (!initialised) {
      buffer = new InputBuffer(256);
      int width = Config.isGreenfoot() ? 80 : Config.getPropInteger("bluej.terminal.width", 80);
      int height = Config.isGreenfoot() ? 10 : Config.getPropInteger("bluej.terminal.height", 22);
      makeWindow(width, height);
      initialised = true;
      text.setUnlimitedBuffering(unlimitedBufferingCall);
    }
  }

  /** Show or hide the Terminal window. */
  public void showHide(boolean show) {
    DataCollector.showHideTerminal(project, show);

    initialise();
    setVisible(show);
    if (show) {
      text.requestFocus();
    }
  }

  /** Return true if the window is currently displayed. */
  public boolean isShown() {
    initialise();
    return isShowing();
  }

  /** Make the window active. */
  public void activate(boolean active) {
    if (active != isActive) {
      initialise();
      text.setEditable(active);
      if (!active) {
        text.getCaret().setVisible(false);
      }
      isActive = active;
    }
  }

  /** Check whether the terminal is active (accepting input). */
  public boolean checkActive() {
    return isActive;
  }

  /** Reset the font according to preferences. */
  public void resetFont() {
    initialise();
    Font terminalFont = getTerminalFont();
    text.setFont(terminalFont);
    if (errorText != null) {
      errorText.setFont(terminalFont);
    }
  }

  /** Clear the terminal. */
  public void clear() {
    initialise();
    text.setText("");
    if (errorText != null) {
      errorText.setText("");
    }
    hideErrorPane();
  }

  /** Save the terminal text to file. */
  public void save() {
    initialise();
    String fileName =
        FileUtility.getFileName(
            this,
            Config.getString("terminal.save.title"),
            Config.getString("terminal.save.buttonText"),
            null,
            false);
    if (fileName != null) {
      File f = new File(fileName);
      if (f.exists()) {
        if (DialogManager.askQuestion(this, "error-file-exists") != 0) return;
      }
      try {
        FileWriter writer = new FileWriter(fileName);
        text.write(writer);
        writer.close();
      } catch (IOException ex) {
        DialogManager.showError(this, "error-save-file");
      }
    }
  }

  public void print() {
    PrinterJob job = PrinterJob.getPrinterJob();
    int printFontSize = Config.getPropInteger("bluej.fontsize.printText", 10);
    Font font = new Font("Monospaced", Font.PLAIN, printFontSize);
    if (job.printDialog()) {
      TerminalPrinter.printTerminal(job, text, job.defaultPage(), font);
    }
  }

  /** Write some text to the terminal. */
  private void writeToPane(boolean stdout, String s) {
    prepare();
    if (!stdout) showErrorPane();

    // The form-feed character should clear the screen.
    int n = s.lastIndexOf('\f');
    if (n != -1) {
      clear();
      s = s.substring(n + 1);
    }

    TermTextArea tta = stdout ? text : errorText;

    tta.append(s);
    tta.setCaretPosition(tta.getDocument().getLength());
  }

  public void writeToTerminal(String s) {
    writeToPane(true, s);
  }

  /** Prepare the terminal for I/O. */
  private void prepare() {
    if (newMethodCall) { // prepare only once per method call
      showHide(true);
      newMethodCall = false;
    } else if (Config.isGreenfoot()) {
      // In greenfoot new output should always show the terminal
      if (!isVisible()) {
        showHide(true);
      }
    }
  }

  /** An interactive method call has been made by a user. */
  private void methodCall(String callString) {
    newMethodCall = false;
    if (clearOnMethodCall) {
      clear();
    }
    if (recordMethodCalls) {
      text.appendMethodCall(callString + "\n");
    }
    newMethodCall = true;
  }

  private void constructorCall(InvokerRecord ir) {
    newMethodCall = false;
    if (clearOnMethodCall) {
      clear();
    }
    if (recordMethodCalls) {
      String callString =
          ir.getResultTypeString() + " " + ir.getResultName() + " = " + ir.toExpression() + ";";
      text.appendMethodCall(callString + "\n");
    }
    newMethodCall = true;
  }

  private void methodResult(ExecutionEvent event) {
    if (recordMethodCalls) {
      String result = null;
      String resultType = event.getResult();

      if (resultType == ExecutionEvent.NORMAL_EXIT) {
        DebuggerObject object = event.getResultObject();
        if (object != null) {
          if (event.getClassName() != null && event.getMethodName() == null) {
            // Constructor call - the result object is the created object.
            // Don't display the result separately:
            return;
          } else {
            // if the method returns a void, we must handle it differently
            if (object.isNullObject()) {
              return; // Don't show result of void calls
            } else {
              // other - the result object is a wrapper with a single result field
              DebuggerField resultField = object.getField(0);
              result = "    returned " + resultField.getType().toString(true) + " ";
              result += resultField.getValueString();
            }
          }
        }
      } else if (resultType == ExecutionEvent.EXCEPTION_EXIT) {
        result = "    Exception occurred.";
      } else if (resultType == ExecutionEvent.TERMINATED_EXIT) {
        result = "    VM terminated.";
      }

      if (result != null) {
        text.appendMethodCall(result + "\n");
      }
    }
  }

  /** Return the input stream that can be used to read from this terminal. */
  public Reader getReader() {
    return in;
  }

  /** Return the output stream that can be used to write to this terminal */
  public Writer getWriter() {
    return out;
  }

  /** Return the output stream that can be used to write error output to this terminal */
  public Writer getErrorWriter() {
    return err;
  }

  // ---- KeyListener interface ----

  @Override
  public void keyPressed(KeyEvent event) {
    if (isMacOs) {
      handleFontsizeKeys(event, event.getKeyCode());
    }
  }

  @Override
  public void keyReleased(KeyEvent event) {}

  /**
   * Handle the keys which change the terminal font size.
   *
   * @param event The key event (key pressed/released/typed)
   * @param ch The key code (for pressed/release events) or character (for key typed events)
   */
  private boolean handleFontsizeKeys(KeyEvent event, int ch) {
    boolean handled = false;

    // Note the following works because VK_EQUALS, VK_PLUS and VK_MINUS
    // are actually defined as their ASCII (and thus unicode) equivalent.
    // Since they are final constants this cannot become untrue in the
    // future.

    switch (ch) {
      case KeyEvent.VK_EQUALS: // increase the font size
      case KeyEvent.VK_PLUS: // increase the font size (non-uk keyboards)
        if (event.getModifiers() == SHORTCUT_MASK) {
          PrefMgr.setEditorFontSize(terminalFontSize + 1);
          event.consume();
          handled = true;
          break;
        }

      case KeyEvent.VK_MINUS: // decrease the font size
        if (event.getModifiers() == SHORTCUT_MASK) {
          PrefMgr.setEditorFontSize(terminalFontSize - 1);
          event.consume();
          handled = true;
          break;
        }
    }

    return handled;
  }

  @Override
  public void keyTyped(KeyEvent event) {
    // We handle most things we are interested in here. The InputMap filters out
    // most other unwanted actions (but allows copy/paste).

    char ch = event.getKeyChar();

    if ((!isMacOs) && handleFontsizeKeys(event, ch)) {
      // Note: On Mac OS with Java 7+, we don't see command+= / command+- as a
      // key-typed event and so we handle it in keyReleased instead.
      return;
    }

    if ((event.getModifiers() & Event.META_MASK) != 0) {
      return; // return without consuming the event
    }
    if (isActive) {
      switch (ch) {
        case 4: // CTRL-D (unix/Mac EOF)
        case 26: // CTRL-Z (DOS/Windows EOF)
          buffer.signalEOF();
          writeToTerminal("\n");
          event.consume();
          break;

        case '\b': // backspace
          if (buffer.backSpace()) {
            try {
              int length = text.getDocument().getLength();
              text.replaceRange("", length - 1, length);
            } catch (Exception exc) {
              Debug.reportError("bad location " + exc);
            }
          }
          event.consume();
          break;

        case '\r': // carriage return
        case '\n': // newline
          if (buffer.putChar('\n')) {
            writeToTerminal(String.valueOf(ch));
            buffer.notifyReaders();
          }
          event.consume();
          break;

        default:
          if (ch >= 32) {
            if (buffer.putChar(ch)) {
              writeToTerminal(String.valueOf(ch));
            }
            event.consume();
          }
          break;
      }
    }
  }

  // ---- BlueJEventListener interface ----

  /**
   * Called when a BlueJ event is raised. The event can be any BlueJEvent type. The implementation
   * of this method should check first whether the event type is of interest an return immediately
   * if it isn't.
   *
   * @param eventId A constant identifying the event. One of the event id constants defined in
   *     BlueJEvent.
   * @param arg An event specific parameter. See BlueJEvent for definition.
   */
  @Override
  public void blueJEvent(int eventId, Object arg) {
    initialise();
    if (eventId == BlueJEvent.METHOD_CALL) {
      InvokerRecord ir = (InvokerRecord) arg;
      if (ir.getResultName() != null) {
        constructorCall(ir);
      } else {
        boolean isVoid = ir.hasVoidResult();
        if (isVoid) {
          methodCall(ir.toStatement());
        } else {
          methodCall(ir.toExpression());
        }
      }
    } else if (eventId == BlueJEvent.EXECUTION_RESULT) {
      methodResult((ExecutionEvent) arg);
    }
  }

  // ---- make window frame ----

  /** Create the Swing window. */
  private void makeWindow(int columns, int rows) {
    Image icon = BlueJTheme.getIconImage();
    if (icon != null) {
      setIconImage(icon);
    }
    text = new TermTextArea(rows, columns, buffer, project, this, false);
    final InputMap origInputMap = text.getInputMap();
    text.setInputMap(
        JComponent.WHEN_FOCUSED,
        new InputMap() {
          {
            setParent(origInputMap);
          }

          @Override
          public Object get(KeyStroke keyStroke) {
            Object actionName = super.get(keyStroke);

            if (actionName == null) {
              return null;
            }

            char keyChar = keyStroke.getKeyChar();
            if (keyChar == KeyEvent.CHAR_UNDEFINED || keyChar < 32) {
              // We might want to filter the action
              if ("copy-to-clipboard".equals(actionName)) {
                return actionName;
              }
              if ("paste-from-clipboard".equals(actionName)) {
                // Handled via paste() in TermTextArea
                return actionName;
              }
              return PrefMgr.getFlag(PrefMgr.ACCESSIBILITY_SUPPORT) ? actionName : null;
            }

            return actionName;
          }
        });

    scrollPane = new JScrollPane(text);
    text.setFont(getTerminalFont());
    text.setEditable(false);
    text.setMargin(new Insets(6, 6, 6, 6));
    text.addKeyListener(this);

    getContentPane().add(scrollPane, BorderLayout.CENTER);

    setJMenuBar(makeMenuBar());

    // Close Action when close button is pressed
    addWindowListener(
        new WindowAdapter() {
          @Override
          public void windowClosing(WindowEvent event) {
            Window win = (Window) event.getSource();

            // don't allow them to close the window if the debug machine
            // is running.. tries to stop them from closing down the
            // input window before finishing off input in the terminal
            if (project != null) {
              if (project.getDebugger().getStatus() == Debugger.RUNNING) return;
            }
            DataCollector.showHideTerminal(project, false);
            win.setVisible(false);
          }
        });

    // save position when window is moved
    addComponentListener(
        new ComponentAdapter() {
          @Override
          public void componentMoved(ComponentEvent event) {
            Config.putLocation("bluej.terminal", getLocation());
          }
        });

    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

    setLocation(Config.getLocation("bluej.terminal"));

    pack();
  }

  /** Create a second scrolled text area to the window, for error output. */
  private void createErrorPane() {
    errorText = new TermTextArea(Config.isGreenfoot() ? 20 : 5, 80, null, project, this, true);
    errorScrollPane = new JScrollPane(errorText);
    errorText.setFont(getTerminalFont());
    errorText.setEditable(false);
    errorText.setMargin(new Insets(6, 6, 6, 6));
    errorText.setUnlimitedBuffering(true);

    splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, errorScrollPane);
  }

  /** Show the errorPane for error output */
  private void showErrorPane() {
    if (errorShown) {
      return;
    }

    // the first time the errortext is shown we need to pack() it
    // to make it have the right size.
    boolean isFirstShow = false;
    if (errorText == null) {
      isFirstShow = true;
      createErrorPane();
    }

    getContentPane().remove(scrollPane);

    // We want to know if it is not the first time
    // This means a "clear" has been used to remove the splitpane
    // when this re-adds the scrollPane to the terminal area
    // it implicitly removes it from the splitpane as it can only have one
    // owner. The side-effect of this is the splitpane's
    // top component becomes null.
    if (!isFirstShow) splitPane.setTopComponent(scrollPane);
    getContentPane().add(splitPane, BorderLayout.CENTER);
    splitPane.resetToPreferredSizes();

    if (isFirstShow) {
      pack();
    } else {
      validate();
    }

    errorShown = true;
  }

  /** Hide the pane with the error output. */
  private void hideErrorPane() {
    if (!errorShown) {
      return;
    }
    getContentPane().remove(splitPane);
    getContentPane().add(scrollPane, BorderLayout.CENTER);
    errorShown = false;
    validate();
  }

  /** Create the terminal's menubar, all menus and items. */
  private JMenuBar makeMenuBar() {
    JMenuBar menubar = new JMenuBar();
    JMenu menu = new JMenu(Config.getString("terminal.options"));
    JMenuItem item;
    item = menu.add(new ClearAction());
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_K, SHORTCUT_MASK));
    item = menu.add(getCopyAction());
    item.setText(Config.getString("terminal.copy"));
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_MASK));
    item = menu.add(new SaveAction());
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_MASK));
    menu.add(new PrintAction());
    menu.add(new JSeparator());

    autoClear = new JCheckBoxMenuItem(new AutoClearAction());
    autoClear.setSelected(clearOnMethodCall);
    menu.add(autoClear);

    recordCalls = new JCheckBoxMenuItem(new RecordCallAction());
    recordCalls.setSelected(recordMethodCalls);
    menu.add(recordCalls);

    unlimitedBuffering = new JCheckBoxMenuItem(new BufferAction());
    unlimitedBuffering.setSelected(unlimitedBufferingCall);
    menu.add(unlimitedBuffering);

    menu.add(new JSeparator());
    item = menu.add(new CloseAction());
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, SHORTCUT_MASK));

    menubar.add(menu);
    return menubar;
  }

  private class ClearAction extends AbstractAction {
    public ClearAction() {
      super(Config.getString("terminal.clear"));
    }

    public void actionPerformed(ActionEvent e) {
      clear();
    }
  }

  private class SaveAction extends AbstractAction {
    public SaveAction() {
      super(Config.getString("terminal.save"));
    }

    public void actionPerformed(ActionEvent e) {
      save();
    }
  }

  private class PrintAction extends AbstractAction {
    public PrintAction() {
      super(Config.getString("terminal.print"));
    }

    public void actionPerformed(ActionEvent e) {
      print();
    }
  }

  private class CloseAction extends AbstractAction {
    public CloseAction() {
      super(Config.getString("terminal.close"));
    }

    public void actionPerformed(ActionEvent e) {
      showHide(false);
    }
  }

  private Action getCopyAction() {
    Action[] textActions = text.getActions();
    for (int i = 0; i < textActions.length; i++) {
      if (textActions[i].getValue(Action.NAME).equals("copy-to-clipboard")) {
        return textActions[i];
      }
    }

    return null;
  }

  private class AutoClearAction extends AbstractAction {
    public AutoClearAction() {
      super(Config.getString("terminal.clearScreen"));
    }

    public void actionPerformed(ActionEvent e) {
      clearOnMethodCall = autoClear.isSelected();
      Config.putPropBoolean(CLEARONMETHODCALLSPROPNAME, clearOnMethodCall);
    }
  }

  private class RecordCallAction extends AbstractAction {
    public RecordCallAction() {
      super(Config.getString("terminal.recordCalls"));
    }

    public void actionPerformed(ActionEvent e) {
      recordMethodCalls = recordCalls.isSelected();
      Config.putPropBoolean(RECORDMETHODCALLSPROPNAME, recordMethodCalls);
    }
  }

  private class BufferAction extends AbstractAction {
    public BufferAction() {
      super(Config.getString("terminal.buffering"));
    }

    public void actionPerformed(ActionEvent e) {
      unlimitedBufferingCall = unlimitedBuffering.isSelected();
      text.setUnlimitedBuffering(unlimitedBufferingCall);
      Config.putPropBoolean(UNLIMITEDBUFFERINGCALLPROPNAME, unlimitedBufferingCall);
    }
  }

  /** A Reader which reads from the terminal. */
  private class TerminalReader extends Reader {
    public int read(char[] cbuf, int off, int len) {
      initialise();
      int charsRead = 0;

      while (charsRead < len) {
        cbuf[off + charsRead] = buffer.getChar();
        charsRead++;
        if (buffer.isEmpty()) break;
      }
      return charsRead;
    }

    @Override
    public boolean ready() {
      return !buffer.isEmpty();
    }

    public void close() {}
  }

  /**
   * A writer which writes to the terminal. It can be flagged for error output. The idea is that
   * error output could be presented differently from standard output.
   */
  private class TerminalWriter extends Writer {
    private boolean isErrorOut;

    TerminalWriter(boolean isError) {
      super();
      isErrorOut = isError;
    }

    public void write(final char[] cbuf, final int off, final int len) {
      try {
        // We use invokeAndWait so that terminal output is limited to
        // the processing speed of the event queue. This means the UI
        // will still respond to user input even if the output is really
        // gushing.
        EventQueue.invokeAndWait(
            new Runnable() {
              public void run() {
                initialise();
                writeToPane(!isErrorOut, new String(cbuf, off, len));
              }
            });
      } catch (InvocationTargetException ite) {
        ite.printStackTrace();
      } catch (InterruptedException ie) {
      }
    }

    public void flush() {}

    public void close() {}
  }
}
Exemple #27
0
 public void actionPerformed(ActionEvent e) {
   unlimitedBufferingCall = unlimitedBuffering.isSelected();
   text.setUnlimitedBuffering(unlimitedBufferingCall);
   Config.putPropBoolean(UNLIMITEDBUFFERINGCALLPROPNAME, unlimitedBufferingCall);
 }
Exemple #28
0
 public BufferAction() {
   super(Config.getString("terminal.buffering"));
 }
Exemple #29
0
 public void actionPerformed(ActionEvent e) {
   recordMethodCalls = recordCalls.isSelected();
   Config.putPropBoolean(RECORDMETHODCALLSPROPNAME, recordMethodCalls);
 }
Exemple #30
0
 public RecordCallAction() {
   super(Config.getString("terminal.recordCalls"));
 }