/** * 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; }
/** * Adds a single item to this roles popup menu. * * <p>This method is used by ClassTarget to add some standard menus as well as by the roles to add * menus. It should be overridden with caution. * * @param menu the popup menu the item is to be added to * @param action the action to be registered with this menu item * @param itemString the String to be displayed on menu item * @param enabled boolean value representing whether item should be enabled */ public void addMenuItem(JPopupMenu menu, Action action, boolean enabled) { JMenuItem item; item = new JMenuItem(); item.setAction(action); item.setFont(PrefMgr.getPopupMenuFont()); item.setForeground(envOpColour); item.setEnabled(enabled); menu.add(item); }
/** * Create the menu items for the given members (constructors or methods). * * @return true if any items were created */ public static boolean createMenuItems( JPopupMenu menu, CallableView[] members, ViewFilter filter, int first, int last, String prefix, InvokeListener il) { // Debug.message("Inside ClassTarget.createMenuItems\n first = " + first // + " last = " + last); boolean hasEntries = false; JMenuItem item; for (int i = first; i < last; i++) { try { CallableView m = members[last - i - 1]; if (!filter.accept(m)) continue; // Debug.message("createSubMenu - creating MenuItem"); Action callAction = null; if (m instanceof MethodView) callAction = new InvokeAction((MethodView) m, il, prefix + m.getLongDesc()); else if (m instanceof ConstructorView) callAction = new ConstructAction((ConstructorView) m, il, prefix + m.getLongDesc()); if (callAction != null) { item = menu.add(callAction); item.setFont(PrefMgr.getPopupMenuFont()); hasEntries = true; } } catch (Exception e) { Debug.reportError("Exception accessing methods: " + e); e.printStackTrace(); } } return hasEntries; }
/** * 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() {} } }
/** 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); }
private void addMenuItem(JPopupMenu menu, Action action) { JMenuItem item = menu.add(action); item.setFont(PrefMgr.getPopupMenuFont()); item.setForeground(envOpColour); }
/** * Sets the value of expanded/collapsed to the current view and to the prefmgr * * @param expanded */ protected void setExpanded(boolean expanded) { // saving the value of the naviview (expanded/collapsed) to the prefmgr // so it is there when the next editor may be opened PrefMgr.setNaviviewExpanded(expanded); this.expanded = expanded; }
/** * Class to provide simple UI customisations such as colours and fonts. Specifically created to * allow access to default Fonts for user interface components for i18n purposes. * * @author Bruce Quig * @version $Id: BlueJTheme.java 6215 2009-03-30 13:28:25Z polle $ */ public class BlueJTheme extends DefaultMetalTheme { private final FontUIResource controlFont = new FontUIResource(PrefMgr.getStandardFont()); private final FontUIResource systemFont = new FontUIResource(controlFont); private final FontUIResource userFont = new FontUIResource(controlFont); private final FontUIResource menuFont = new FontUIResource(PrefMgr.getStandardMenuFont()); // icon to be used for BlueJ windows private static Image iconImage = null; // common strings - must be accessed through getOkLabel() private static String okayLabel; private static String cancelLabel; private static String closeLabel; private static String continueLabel; // a dimension for ok and cancel buttons that is as large as // needed to display either private static Dimension okCancelDimension; // JSplitPane divider width constant public static final int splitPaneDividerWidth = 3; // Other general spacing constants. We should try to use these for consistency public static final int generalSpacingWidth = 5; public static final Border generalBorder = BorderFactory.createEmptyBorder(10, 10, 10, 10); public static final Border generalBorderWithStatusBar = BorderFactory.createEmptyBorder(10, 10, 0, 10); public static final Border dialogBorder = BorderFactory.createEmptyBorder(12, 12, 12, 12); private static Border roundedShadowBorder; private static Border shadowBorder; public static final int commandButtonSpacing = 5; public static final int commandButtonPadding = 12; public static final int componentSpacingSmall = 5; public static final int componentSpacingLarge = 11; public static final int dialogCommandButtonsVertical = 17; /** Name of theme */ public String getName() { return "BlueJTheme"; } public FontUIResource getControlTextFont() { return controlFont; } public FontUIResource getSystemTextFont() { return systemFont; } public FontUIResource getUserTextFont() { return userFont; } public FontUIResource getMenuTextFont() { return menuFont; } /** * Get the icon for most BlueJ frames. * * @return an icon to be used as the frame icon for most BlueJ windows */ public static Image getIconImage() { if (iconImage == null) iconImage = Config.getImageAsIcon("image.icon").getImage(); return iconImage; } public static void setIconImage(Image newIconImage) { iconImage = newIconImage; } public static String getOkLabel() { if (okayLabel == null) { okayLabel = Config.getString("okay"); } return okayLabel; } public static String getCancelLabel() { if (cancelLabel == null) { cancelLabel = Config.getString("cancel"); } return cancelLabel; } public static String getCloseLabel() { if (closeLabel == null) { closeLabel = Config.getString("close"); } return closeLabel; } public static String getContinueLabel() { if (continueLabel == null) { continueLabel = Config.getString("continue"); } return continueLabel; } /** * Get a standard BlueJ "ok" button. * * @return A JButton that says "ok" */ public static JButton getOkButton() { computeButtonWidths(); JButton okButton = new JButton(getOkLabel()); // try to make the OK, cancel and continue buttons have equal size okButton.setPreferredSize(okCancelDimension); return okButton; } /** * Get a standard BlueJ "cancel" button. * * @return A JButton that says "cancel" */ public static JButton getCancelButton() { computeButtonWidths(); JButton cancelButton = new JButton(getCancelLabel()); // try to make the OK, cancel and continue buttons have equal size cancelButton.setPreferredSize(okCancelDimension); return cancelButton; } /** * Get a standard BlueJ "close" button. * * @return A JButton that says "cancel" */ public static JButton getCloseButton() { computeButtonWidths(); JButton closeButton = new JButton(getCloseLabel()); // try to make the OK, cancel and continue buttons have equal size closeButton.setPreferredSize(okCancelDimension); return closeButton; } /** * Get a standard BlueJ "continue" button. * * @return A JButton that says "Continue" */ public static JButton getContinueButton() { computeButtonWidths(); JButton continueButton = new JButton(getContinueLabel()); // try to make the OK, cancel and continue buttons have equal size continueButton.setPreferredSize(okCancelDimension); return continueButton; } public static synchronized Border getRoundedShadowBorder() { if (roundedShadowBorder == null) { roundedShadowBorder = new RoundedShadowBorder(); } return roundedShadowBorder; } public static synchronized Border getShadowBorder() { if (roundedShadowBorder == null) { shadowBorder = new ShadowBorder(); } return roundedShadowBorder; } /** * Computer the maximum width of the ok, cancel and continue buttons and set the okCancelDimension * to be representative of that size. */ private static void computeButtonWidths() { if (okCancelDimension != null) return; JButton okButton = new JButton(getOkLabel()); JButton cancelButton = new JButton(getCancelLabel()); JButton continueButton = new JButton(getContinueLabel()); int maxWidth = Math.max(cancelButton.getPreferredSize().width, okButton.getPreferredSize().width); maxWidth = Math.max(maxWidth, continueButton.getPreferredSize().width); okCancelDimension = new Dimension(maxWidth, okButton.getPreferredSize().height); } /** * A border with rounded corners and a shadow * * @author Poul Henriksen */ private static class RoundedShadowBorder extends AbstractBorder { private Insets insets; private ImageIcon topLeftCorner = Config.getImageAsIcon("image.border.topleft"); private ImageIcon topRightCorner = Config.getImageAsIcon("image.border.topright"); private ImageIcon bottomLeftCorner = Config.getImageAsIcon("image.border.bottomleft"); private ImageIcon bottomRightCorner = Config.getImageAsIcon("image.border.bottomright"); private Color shadowColor = new Color(145, 145, 145); private Color backgroundColor = Color.white; private Color borderColor = Color.black; private int backgroundThickness = 5; // extra space around the frame public RoundedShadowBorder() { insets = new Insets(0, 0, 0, 0); insets.bottom = bottomLeftCorner.getIconHeight() + backgroundThickness; insets.top = topLeftCorner.getIconHeight() + backgroundThickness; insets.left = topLeftCorner.getIconHeight() + backgroundThickness; insets.right = topRightCorner.getIconHeight() + backgroundThickness; } /** * Reinitializes the insets parameter with this Border's current Insets. * * @param c the component for which this border insets value applies * @param insets the object to be reinitialized * @return the <code>insets</code> object */ public Insets getBorderInsets(Component c, Insets insets) { insets.bottom = this.insets.bottom; insets.top = this.insets.top; insets.left = this.insets.left; insets.right = this.insets.right; return insets; } /** * Returns a new <code>Insets</code> instance. * * @param c the component for which this border insets value applies * @return the new <code>Insets</code> object */ public Insets getBorderInsets(Component c) { return (Insets) insets.clone(); } /** Returns false. */ public boolean isBorderOpaque() { return false; } /** Paints the border */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { x += backgroundThickness; y += backgroundThickness; height -= 2 * backgroundThickness; width -= 2 * backgroundThickness; // Top g.setColor(backgroundColor); g.drawLine(x, y, x + width, y); g.drawLine(x, y + 1, x + width, y + 1); g.drawLine(x, y + 2, x + width, y + 2); g.setColor(borderColor); g.drawLine(x, y + 3, x + width, y + 3); // Bottom g.setColor(borderColor); g.drawLine(x, y + height - 4, x + width, y + height - 4); g.setColor(shadowColor); g.drawLine(x, y + height - 3, x + width, y + height - 3); g.drawLine(x, y + height - 2, x + width, y + height - 2); // Left g.setColor(backgroundColor); g.drawLine(x, y, x, y + height); g.drawLine(x + 1, y, x + 1, y + height); g.drawLine(x + 2, y, x + 2, y + height); g.setColor(borderColor); g.drawLine(x + 3, y, x + 3, y + height); // Right g.setColor(borderColor); g.drawLine(x + width - 4, y, x + width - 4, y + height); g.setColor(shadowColor); g.drawLine(x + width - 3, y, x + width - 3, y + height); g.drawLine(x + width - 2, y, x + width - 2, y + height); // Background around the border g.setColor(backgroundColor); for (int i = 0; i < backgroundThickness + 1; i++) { g.drawRect(x - i, y - i, width - 1 + 2 * i, height - 1 + 2 * i); } // Corners height += backgroundThickness; width += backgroundThickness; topLeftCorner.paintIcon(c, g, x, y); topRightCorner.paintIcon(c, g, x + width - insets.right, y); bottomLeftCorner.paintIcon(c, g, x, y + height - insets.bottom); bottomRightCorner.paintIcon(c, g, x + width - insets.right, y + height - insets.bottom); } } /** * A border with rounded corners and a shadow * * * @author Poul Henriksen */ private static class ShadowBorder extends AbstractBorder { private Insets insets; private Color shadowColor = new Color(145, 145, 145); private Color backgroundColor = Color.white; private Color borderColor = Color.black; private int backgroundThickness = 5; // extra space around the frame public ShadowBorder() { insets = new Insets( 4 + backgroundThickness, 4 + backgroundThickness, 4 + backgroundThickness, 4 + backgroundThickness); } /** * Reinitializes the insets parameter with this Border's current Insets. * * @param c the component for which this border insets value applies * @param insets the object to be reinitialized * @return the <code>insets</code> object */ public Insets getBorderInsets(Component c, Insets insets) { insets.bottom = this.insets.bottom; insets.top = this.insets.top; insets.left = this.insets.left; insets.right = this.insets.right; return insets; } /** * Returns a new <code>Insets</code> instance. * * @param c the component for which this border insets value applies * @return the new <code>Insets</code> object */ public Insets getBorderInsets(Component c) { return (Insets) insets.clone(); } /** Returns false. */ public boolean isBorderOpaque() { return false; } /** Paints the border */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { x += backgroundThickness; y += backgroundThickness; height -= 2 * backgroundThickness; width -= 2 * backgroundThickness; // Right g.setColor(borderColor); g.drawLine(x + width - 4, y, x + width - 4, y + height); g.setColor(shadowColor); g.drawLine(x + width - 3, y, x + width - 3, y + height); g.drawLine(x + width - 2, y, x + width - 2, y + height); g.setColor(backgroundColor); g.drawLine(x + width - 1, y, x + width - 1, y + height); g.fillRect(x + width - 3, y, x + width, y + 5); // Bottom g.setColor(borderColor); g.drawLine(x + 3, y + height - 4, x + width - 4, y + height - 4); g.setColor(shadowColor); g.drawLine(x, y + height - 3, x + width - 2, y + height - 3); g.drawLine(x, y + height - 2, x + width - 2, y + height - 2); g.setColor(backgroundColor); g.drawLine(x, y + height - 1, x + width, y + height - 1); g.fillRect(x, y + height - 3, x + 5, y + height); // Top g.setColor(backgroundColor); g.drawLine(x, y, x + width, y); g.drawLine(x, y + 1, x + width, y + 1); g.drawLine(x, y + 2, x + width, y + 2); g.setColor(borderColor); g.drawLine(x, y + 3, x + width - 4, y + 3); // Left g.setColor(backgroundColor); g.drawLine(x, y, x, y + height); g.drawLine(x + 1, y, x + 1, y + height); g.drawLine(x + 2, y, x + 2, y + height); g.setColor(borderColor); g.drawLine(x + 3, y + 3, x + 3, y + height - 4); // Background around the border g.setColor(backgroundColor); for (int i = 0; i < backgroundThickness + 1; i++) { g.drawRect(x - i, y - i, width - 1 + 2 * i, height - 1 + 2 * i); } } } }