/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2004-05 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA $Id$ */ package processing.app; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * Panel just below the editing area that contains status messages. */ public class EditorStatus extends JPanel implements ActionListener { static Color bgcolor[]; static Color fgcolor[]; static final int NOTICE = 0; static final int ERR = 1; static final int PROMPT = 2; static final int EDIT = 3; static final int SERIAL = 4; static final int YES = 1; static final int NO = 2; static final int CANCEL = 3; static final int OK = 4; static final int SEND = 5; static final String NO_MESSAGE = ""; Editor editor; int mode; String message; Font font; FontMetrics metrics; int ascent; Image offscreen; int sizeW, sizeH; int imageW, imageH; JButton yesButton; JButton noButton; JButton cancelButton; JButton okButton; JButton sendButton; JTextField editField; JTextField serialField; JComboBox serialRates; //Thread promptThread; int response; public EditorStatus(Editor editor) { this.editor = editor; empty(); if (bgcolor == null) { bgcolor = new Color[5]; // Arduino 0003 switched to a blue color scheme to visually distinguish // itself from Processing. Because the image files for certain interface // elements (e.g. buttons and tabs) are distributed with the application // while the preference file that specifies the IDE colors is stored in // the user's home directory and shared across all versions of Arduino, // we need to hardcode certain colors here to match the images. // Otherwise, users who used different multiple versions of the software // would sometimes see colors that didn't match the interface elements. // This is a hack and prevents users from customizing the IDE colors, // however, it obviates the need to provide for version-specific // preferences. //bgcolor[0] = Preferences.getColor("status.notice.bgcolor"); bgcolor[0] = new Color(0x54, 0x91, 0x9e); bgcolor[1] = Preferences.getColor("status.error.bgcolor"); bgcolor[2] = Preferences.getColor("status.prompt.bgcolor"); bgcolor[3] = Preferences.getColor("status.prompt.bgcolor"); bgcolor[4] = new Color(0x54, 0x91, 0x9e); fgcolor = new Color[5]; fgcolor[0] = Preferences.getColor("status.notice.fgcolor"); fgcolor[1] = Preferences.getColor("status.error.fgcolor"); fgcolor[2] = Preferences.getColor("status.prompt.fgcolor"); fgcolor[3] = Preferences.getColor("status.prompt.fgcolor"); fgcolor[4] = Preferences.getColor("status.notice.fgcolor"); } } public void empty() { mode = NOTICE; message = NO_MESSAGE; //update(); repaint(); } public void notice(String message) { mode = NOTICE; this.message = message; //update(); repaint(); } public void unnotice(String unmessage) { if (message.equals(unmessage)) empty(); } public void error(String message) { mode = ERR; this.message = message; repaint(); } public void prompt(String message) { mode = PROMPT; this.message = message; response = 0; yesButton.setVisible(true); noButton.setVisible(true); cancelButton.setVisible(true); yesButton.requestFocus(); repaint(); } // prompt has been handled, re-hide the buttons public void unprompt() { yesButton.setVisible(false); noButton.setVisible(false); cancelButton.setVisible(false); empty(); } public void edit(String message, String dflt) { mode = EDIT; this.message = message; response = 0; okButton.setVisible(true); cancelButton.setVisible(true); editField.setVisible(true); editField.setText(dflt); editField.selectAll(); editField.requestFocus(); repaint(); } public void unedit() { okButton.setVisible(false); cancelButton.setVisible(false); editField.setVisible(false); empty(); } public void serial() { mode = SERIAL; this.message = NO_MESSAGE; sendButton.setVisible(true); serialRates.setVisible(true); serialField.setVisible(true); serialField.setText(""); serialField.requestFocus(); repaint(); } public void unserial() { sendButton.setVisible(false); serialField.setVisible(false); serialRates.setVisible(false); empty(); } /* public void update() { Graphics g = this.getGraphics(); try { setBackground(bgcolor[mode]); } catch (NullPointerException e) { } // if not ready yet if (g != null) paint(g); } public void update(Graphics g) { paint(g); } */ public void paintComponent(Graphics screen) { //if (screen == null) return; if (yesButton == null) setup(); //System.out.println("status.paintComponent"); Dimension size = getSize(); if ((size.width != sizeW) || (size.height != sizeH)) { // component has been resized if ((size.width > imageW) || (size.height > imageH)) { // nix the image and recreate, it's too small offscreen = null; } else { // who cares, just resize sizeW = size.width; sizeH = size.height; setButtonBounds(); } } if (offscreen == null) { sizeW = size.width; sizeH = size.height; setButtonBounds(); imageW = sizeW; imageH = sizeH; offscreen = createImage(imageW, imageH); } Graphics g = offscreen.getGraphics(); if (font == null) { font = Preferences.getFont("status.font"); //new Font("SansSerif", Font.PLAIN, 12)); g.setFont(font); metrics = g.getFontMetrics(); ascent = metrics.getAscent(); } //setBackground(bgcolor[mode]); // does nothing g.setColor(bgcolor[mode]); g.fillRect(0, 0, imageW, imageH); g.setColor(fgcolor[mode]); g.setFont(font); // needs to be set each time on osx g.drawString(message, Preferences.GUI_SMALL, (sizeH + ascent) / 2); screen.drawImage(offscreen, 0, 0, null); } protected void setup() { if (yesButton == null) { yesButton = new JButton(Preferences.PROMPT_YES); noButton = new JButton(Preferences.PROMPT_NO); cancelButton = new JButton(Preferences.PROMPT_CANCEL); okButton = new JButton(Preferences.PROMPT_OK); sendButton = new JButton(Preferences.PROMPT_SEND); // !@#(* aqua ui #($*(( that turtle-neck wearing #(** (#$@)( // os9 seems to work if bg of component is set, but x still a bastard if (Base.isMacOS()) { yesButton.setBackground(bgcolor[PROMPT]); noButton.setBackground(bgcolor[PROMPT]); cancelButton.setBackground(bgcolor[PROMPT]); okButton.setBackground(bgcolor[PROMPT]); sendButton.setBackground(bgcolor[SERIAL]); } setLayout(null); yesButton.addActionListener(this); noButton.addActionListener(this); cancelButton.addActionListener(this); okButton.addActionListener(this); sendButton.addActionListener(this); add(yesButton); add(noButton); add(cancelButton); add(okButton); add(sendButton); yesButton.setVisible(false); noButton.setVisible(false); cancelButton.setVisible(false); okButton.setVisible(false); sendButton.setVisible(false); editField = new JTextField(); editField.addActionListener(this); //if (Base.platform != Base.MACOSX) { editField.addKeyListener(new KeyAdapter() { // no-op implemented because of a jikes bug //protected void noop() { } //public void keyPressed(KeyEvent event) { //System.out.println("pressed " + event + " " + KeyEvent.VK_SPACE); //} // use keyTyped to catch when the feller is actually // added to the text field. with keyTyped, as opposed to // keyPressed, the keyCode will be zero, even if it's // enter or backspace or whatever, so the keychar should // be used instead. grr. public void keyTyped(KeyEvent event) { //System.out.println("got event " + event + " " + // KeyEvent.VK_SPACE); int c = event.getKeyChar(); if (mode == EDIT) { if (c == KeyEvent.VK_ENTER) { // accept the input String answer = editField.getText(); editor.sketch.nameCode(answer); unedit(); event.consume(); // easier to test the affirmative case than the negative } else if ((c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE) || (c == KeyEvent.VK_RIGHT) || (c == KeyEvent.VK_LEFT) || (c == KeyEvent.VK_UP) || (c == KeyEvent.VK_DOWN) || (c == KeyEvent.VK_HOME) || (c == KeyEvent.VK_END) || (c == KeyEvent.VK_SHIFT)) { //System.out.println("nothing to see here"); //noop(); } else if (c == KeyEvent.VK_ESCAPE) { unedit(); editor.buttons.clear(); event.consume(); } else if (c == KeyEvent.VK_SPACE) { //System.out.println("got a space"); // if a space, insert an underscore //editField.insert("_", editField.getCaretPosition()); /* tried to play nice and see where it got me editField.dispatchEvent(new KeyEvent(editField, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, 45, '_')); */ //System.out.println("start/end = " + // editField.getSelectionStart() + " " + // editField.getSelectionEnd()); String t = editField.getText(); //int p = editField.getCaretPosition(); //editField.setText(t.substring(0, p) + "_" + t.substring(p)); //editField.setCaretPosition(p+1); int start = editField.getSelectionStart(); int end = editField.getSelectionEnd(); editField.setText(t.substring(0, start) + "_" + t.substring(end)); editField.setCaretPosition(start+1); //System.out.println("consuming event"); event.consume(); } else if ((c == '_') || (c == '.') || // allow .pde and .java ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) { // everything fine, catches upper and lower //noop(); } else if ((c >= '0') && (c <= '9')) { // getCaretPosition == 0 means that it's the first char // and the field is empty. // getSelectionStart means that it *will be* the first // char, because the selection is about to be replaced // with whatever is typed. if ((editField.getCaretPosition() == 0) || (editField.getSelectionStart() == 0)) { // number not allowed as first digit //System.out.println("bad number bad"); event.consume(); } } else { event.consume(); //System.out.println("code is " + code + " char = " + c); } } //System.out.println("code is " + code + " char = " + c); } }); add(editField); editField.setVisible(false); serialField = new JTextField(); serialField.addActionListener(this); serialField.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent event) { int c = event.getKeyChar(); if (c == KeyEvent.VK_ENTER) { // accept the input editor.serialPort.write(serialField.getText()); event.consume(); serialField.setText(""); } }}); add(serialField); serialField.setVisible(false); String[] serialRateStrings = { "300","1200","2400","4800","9600","14400", "19200","28800","38400","57600","115200" }; serialRates = new JComboBox(); if (Base.isMacOS()) serialRates.setBackground(bgcolor[SERIAL]); for (int i = 0; i < serialRateStrings.length; i++) serialRates.addItem(serialRateStrings[i] + " baud"); serialRates.setSelectedItem( Preferences.get("serial.debug_rate") + " baud"); serialRates.addActionListener(this); add(serialRates); serialRates.setVisible(false); } } protected void setButtonBounds() { int top = (sizeH - Preferences.BUTTON_HEIGHT) / 2; int eachButton = Preferences.GUI_SMALL + Preferences.BUTTON_WIDTH; int cancelLeft = sizeW - eachButton; int noLeft = cancelLeft - eachButton; int yesLeft = noLeft - eachButton; yesButton.setLocation(yesLeft, top); noButton.setLocation(noLeft, top); cancelButton.setLocation(cancelLeft, top); editField.setLocation(yesLeft - Preferences.BUTTON_WIDTH, top); serialField.setLocation(yesLeft - Preferences.BUTTON_WIDTH, top); okButton.setLocation(noLeft, top); serialRates.setLocation(0, top); sendButton.setLocation(cancelLeft, top); yesButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); noButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); cancelButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); okButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); sendButton.setSize( Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); serialRates.setSize( 3*Preferences.BUTTON_WIDTH/2, Preferences.BUTTON_HEIGHT); editField.setSize( 2*Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); serialField.setSize( 3*Preferences.BUTTON_WIDTH, Preferences.BUTTON_HEIGHT); } public Dimension getPreferredSize() { return getMinimumSize(); } public Dimension getMinimumSize() { return new Dimension(300, Preferences.GRID_SIZE); } public Dimension getMaximumSize() { return new Dimension(3000, Preferences.GRID_SIZE); } public void actionPerformed(ActionEvent e) { if (e.getSource() == noButton) { // shut everything down, clear status, and return unprompt(); // don't need to save changes editor.checkModified2(); } else if (e.getSource() == yesButton) { // answer was in response to "save changes?" unprompt(); editor.handleSave(true); editor.checkModified2(); } else if (e.getSource() == cancelButton) { // don't do anything, don't continue with checkModified2 if (mode == PROMPT) unprompt(); else if (mode == EDIT) unedit(); editor.buttons.clear(); } else if (e.getSource() == okButton) { // answering to "save as..." question String answer = editField.getText(); //editor.handleSaveAs2(answer); editor.sketch.nameCode(answer); unedit(); } else if (e.getSource() == sendButton) { editor.serialPort.write(serialField.getText()); serialField.setText(""); } else if (e.getSource() == serialRates) { String wholeString = (String) serialRates.getSelectedItem(); String rateString = wholeString.substring(0, wholeString.indexOf(' ')); int rate = Integer.parseInt(rateString); Preferences.set("serial.debug_rate", rateString); editor.serialPort.dispose(); try { editor.serialPort = new Serial(true); } catch (SerialException err) { editor.error(err); } } } }