/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2004-10 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. 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 */ package processing.app; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; /** * run/stop/etc buttons for the ide */ public class EditorToolbar extends JComponent implements MouseInputListener, KeyListener { /** Rollover titles for each button. */ static final String title[] = { "Verify", "Schematics", "New", "Open", "Save", "Upload to Wiring hardware", "Serial monitor" }; /** Titles for each button when the shift key is pressed. */ static final String titleShift[] = { "Verify", "Schematics", "New Editor Window", "Open in Another Window", "Save", "Upload to Wiring hardware", "Serial monitor" }; static final int BUTTON_COUNT = title.length; /** Width of each toolbar button. */ static final int BUTTON_WIDTH = 27; /** Height of each toolbar button. */ static final int BUTTON_HEIGHT = 32; /** The amount of space between groups of buttons on the toolbar. */ static final int BUTTON_GAP = 5; /** Size of the button image being chopped up. */ static final int BUTTON_IMAGE_SIZE = 33; static final int RUN = 0; static final int SCHEMATICS = 1; static final int NEW = 2; static final int OPEN = 3; static final int SAVE = 4; static final int EXPORT = 5; static final int SERIAL = 6; static final int INACTIVE = 0; static final int ROLLOVER = 1; static final int ACTIVE = 2; static final int DISABLED = 3; Editor editor; Image offscreen; int width, height; Color bgcolor; static Image[][] buttonImages; int currentRollover; JPopupMenu popup; JMenu menu; int buttonCount; int[] state = new int[BUTTON_COUNT]; Image[] stateImage; int which[]; // mapping indices to implementation int x1[], x2[]; int y1, y2; Font statusFont; Color statusColor; boolean shiftPressed; public EditorToolbar(Editor editor, JMenu menu) { this.editor = editor; this.menu = menu; buttonCount = 0; which = new int[BUTTON_COUNT]; //which[buttonCount++] = NOTHING; which[buttonCount++] = RUN; which[buttonCount++] = SCHEMATICS; which[buttonCount++] = NEW; which[buttonCount++] = OPEN; which[buttonCount++] = SAVE; which[buttonCount++] = EXPORT; which[buttonCount++] = SERIAL; currentRollover = -1; bgcolor = Theme.getColor("buttons.bgcolor"); statusFont = Theme.getFont("buttons.status.font"); statusColor = Theme.getColor("buttons.status.color"); addMouseListener(this); addMouseMotionListener(this); } protected void loadButtons() { Image allButtons = Base.getThemeImage("buttons.gif", this); buttonImages = new Image[BUTTON_COUNT][3]; for (int i = 0; i < BUTTON_COUNT; i++) { for (int state = 0; state < 3; state++) { Image image = createImage(BUTTON_WIDTH, BUTTON_HEIGHT); Graphics g = image.getGraphics(); g.drawImage(allButtons, -(i*BUTTON_IMAGE_SIZE) - 3, (-2 + state)*BUTTON_IMAGE_SIZE, null); buttonImages[i][state] = image; } } } @Override public void paintComponent(Graphics screen) { // this data is shared by all EditorToolbar instances if (buttonImages == null) { loadButtons(); } // this happens once per instance of EditorToolbar if (stateImage == null) { stateImage = new Image[buttonCount]; for (int i = 0; i < buttonCount; i++) { stateImage[i] = buttonImages[which[i]][INACTIVE]; } y1 = 0; y2 = BUTTON_HEIGHT; x1 = new int[buttonCount]; x2 = new int[buttonCount]; } Dimension size = getSize(); if ((offscreen == null) || (size.width != width) || (size.height != height)) { offscreen = createImage(size.width, size.height); width = size.width; height = size.height; int offsetX = 3; for (int i = 0; i < buttonCount; i++) { x1[i] = offsetX; if (i == 2) x1[i] += BUTTON_GAP; x2[i] = x1[i] + BUTTON_WIDTH; offsetX = x2[i]; } } Graphics g = offscreen.getGraphics(); g.setColor(bgcolor); //getBackground()); g.fillRect(0, 0, width, height); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); for (int i = 0; i < buttonCount; i++) { g.drawImage(stateImage[i], x1[i], y1, null); } g.setColor(statusColor); g.setFont(statusFont); // If I ever find the guy who wrote the Java2D API, I will hurt him. // Graphics2D g2 = (Graphics2D) g; // FontRenderContext frc = g2.getFontRenderContext(); // float statusW = (float) statusFont.getStringBounds(status, frc).getWidth(); // float statusX = (getSize().width - statusW) / 2; // g2.drawString(status, statusX, statusY); if (currentRollover != -1) { int statusY = (BUTTON_HEIGHT + g.getFontMetrics().getAscent()) / 2; String status = shiftPressed ? titleShift[currentRollover] : title[currentRollover]; g.drawString(status, buttonCount * BUTTON_WIDTH + 3 * BUTTON_GAP, statusY); } screen.drawImage(offscreen, 0, 0, null); if (!isEnabled()) { screen.setColor(new Color(0,0,0,100)); screen.fillRect(0, 0, getWidth(), getHeight()); } } public void mouseMoved(MouseEvent e) { if (!isEnabled()) return; // ignore mouse events before the first paintComponent() call if (state == null) return; if (state[OPEN] != INACTIVE) { // avoid flicker, since there will probably be an update event setState(OPEN, INACTIVE, false); } handleMouse(e); } public void mouseDragged(MouseEvent e) { } public void handleMouse(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (currentRollover != -1) { if ((x > x1[currentRollover]) && (y > y1) && (x < x2[currentRollover]) && (y < y2)) { return; } else { setState(currentRollover, INACTIVE, true); currentRollover = -1; } } int sel = findSelection(x, y); if (sel != -1) { if (state[sel] != ACTIVE) { setState(sel, ROLLOVER, true); currentRollover = sel; } } } private int findSelection(int x, int y) { // if app loads slowly and cursor is near the buttons // when it comes up, the app may not have time to load if ((x1 == null) || (x2 == null)) return -1; for (int i = 0; i < buttonCount; i++) { if ((y > y1) && (x > x1[i]) && (y < y2) && (x < x2[i])) { if (state[i] != DISABLED) { return i; } } } return -1; } private void setState(int slot, int newState, boolean updateAfter) { if (state[slot]!=DISABLED) { state[slot] = newState; stateImage[slot] = buttonImages[which[slot]][newState]; if (updateAfter) { repaint(); } } } public void mouseEntered(MouseEvent e) { handleMouse(e); } public void mouseExited(MouseEvent e) { // if the 'open' popup menu is visible, don't register this, // because the popup being set visible will fire a mouseExited() event if ((popup != null) && popup.isVisible()) return; if (state[OPEN] != INACTIVE) { setState(OPEN, INACTIVE, true); } handleMouse(e); // there is no more rollover, make sure that the rollover text goes away currentRollover = -1; } int wasDown = -1; public void mousePressed(MouseEvent e) { // ignore mouse presses so hitting 'run' twice doesn't cause problems if (!isEnabled()) return; final int x = e.getX(); final int y = e.getY(); int sel = findSelection(x, y); if (sel == -1) return; currentRollover = -1; switch (sel) { case RUN: editor.handleRun(e.isShiftDown()); break; case SCHEMATICS: editor.handleSchematics(); break; case OPEN: popup = menu.getPopupMenu(); popup.show(EditorToolbar.this, x, y); break; case NEW: if (shiftPressed) { editor.base.handleNew(); } else { editor.base.handleNewReplace(); } break; case SAVE: editor.handleSave(false); break; case EXPORT: editor.handleExport(e.isShiftDown()); break; case SERIAL: editor.handleSerial(); break; } } public void mouseClicked(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } /** * Set a particular button to be active. */ public void activate(int what) { if (buttonImages != null) { setState(what, ACTIVE, true); } } /** * Set a particular button to be active. */ public void deactivate(int what) { if (buttonImages != null) { setState(what, INACTIVE, true); } } /** * Set a particular button to be active. */ public void disable(int what) { if (buttonImages != null && which!=null && state!=null && stateImage!=null) { state[what] = DISABLED; stateImage[what] = buttonImages[which[what]][INACTIVE]; repaint(); } } public Dimension getPreferredSize() { return getMinimumSize(); } public Dimension getMinimumSize() { return new Dimension((BUTTON_COUNT + 1)*BUTTON_WIDTH, BUTTON_HEIGHT); } public Dimension getMaximumSize() { return new Dimension(3000, BUTTON_HEIGHT); } public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SHIFT) { shiftPressed = true; repaint(); } } public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SHIFT) { shiftPressed = false; repaint(); } } public void keyTyped(KeyEvent e) { } }