Java tutorial
/** * Copyright (C) 2013 Rusty Gerard * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package com.callidusrobotics.swing; import java.awt.Color; import java.awt.DisplayMode; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.event.KeyEvent; import java.util.Iterator; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import org.apache.commons.lang3.Validate; import com.callidusrobotics.util.TrueColor; @SuppressWarnings("PMD.AvoidUsingVolatile") public final class SwingConsole extends AbstractConsole { // 1000 milliseconds / 30 frames per second = 33 ms delay private static final int REFRESH_RATE = 33; private Graphics graphicsRenderer; private Image imageBuffer; private JFrame frame; private JLabel label; private ImageIcon icon; private int charWidth, charHeight; private int charVOffset, charHOffset; private long lastUpdate = 0; private volatile KeyEvent lastKeyPressed = null; private volatile KeyEvent lastKeyTyped = null; SwingConsole(final Font font, final int rows, final int columns) { super(rows, columns); Validate.notNull(font); initConsole(font, rows, columns); } private void initConsole(final Font font, final int rows, final int cols) { frame = new JFrame(); icon = new ImageIcon(); label = new JLabel(icon); // Compute font metrics final DisplayMode displayMode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice() .getDisplayMode(); frame.setVisible(true); imageBuffer = frame.createImage(displayMode.getWidth(), displayMode.getHeight()); graphicsRenderer = imageBuffer.getGraphics(); graphicsRenderer.setFont(font); final FontMetrics fontMetrics = graphicsRenderer.getFontMetrics(font); charHeight = fontMetrics.getHeight(); charWidth = fontMetrics.getHeight(); charHOffset = 0; charVOffset = fontMetrics.getAscent(); // Compute console dimensions and initialize graphics renderer final int width = charWidth * cols; final int height = charHeight * rows; imageBuffer = frame.createImage(width, height); graphicsRenderer = imageBuffer.getGraphics(); graphicsRenderer.setFont(font); render(); label.setBackground(TrueColor.MAGENTA); label.setBounds(0, 0, width, height); label.setVisible(true); frame.setBounds(0, 0, width, height); frame.setBackground(TrueColor.MAGENTA); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(label); frame.setCursor(null); frame.pack(); frame.setLocationRelativeTo(null); frame.setResizable(false); frame.addKeyListener(this); } @Override public void setTitle(final String title) { frame.setTitle(title); } @Override public void render() { if (refreshSet.size() == 0) { return; } while (System.currentTimeMillis() - lastUpdate < REFRESH_RATE) { Thread.yield(); } final Iterator<Integer> iterator = refreshSet.iterator(); while (iterator.hasNext()) { final int index = iterator.next(); // index = r * cols + c final int row = index / getWidth(); final int col = index % getWidth(); final int hpos = col * charWidth; final int vpos = row * charHeight; graphicsRenderer.setColor(consoleBuffer[row][col].background); graphicsRenderer.fillRect(hpos, vpos, charWidth, charHeight); graphicsRenderer.setColor(consoleBuffer[row][col].foreground); graphicsRenderer.drawString(String.valueOf(consoleBuffer[row][col].character), hpos + charHOffset, vpos + charVOffset); } refreshSet.clear(); if (cursorIsVisible) { final int hpos = cursorCol * charWidth; final int vpos = cursorRow * charHeight; graphicsRenderer.setColor(cursor.foreground); graphicsRenderer.drawString(String.valueOf(cursor.character), hpos + charHOffset, vpos + charVOffset); } graphicsRenderer.drawImage(imageBuffer, 0, 0, null); icon.setImage(imageBuffer); label.setVisible(false); label.setVisible(true); lastUpdate = System.currentTimeMillis(); } @Override @SuppressWarnings("PMD.NullAssignment") public char getKeyPress() { lastKeyPressed = null; while (lastKeyPressed == null) { Thread.yield(); } if (lastKeyPressed.getKeyChar() == KeyEvent.CHAR_UNDEFINED) { return (char) lastKeyPressed.getKeyCode(); } return lastKeyPressed.getKeyChar(); } @SuppressWarnings("PMD.NullAssignment") private char getKeyTyped() { lastKeyTyped = null; while (lastKeyTyped == null) { Thread.yield(); } return lastKeyTyped.getKeyChar(); } @Override public String getline(final int maxLen, final Color foreground, final Color background) throws ArrayIndexOutOfBoundsException { final StringBuffer stringBuffer = new StringBuffer(maxLen); boolean keepPolling = true; while (keepPolling) { if (stringBuffer.length() < maxLen) { showCursor(); } else { hideCursor(); } render(); final char input = getKeyTyped(); switch (input) { case KeyEvent.VK_ENTER: keepPolling = false; break; case KeyEvent.VK_BACK_SPACE: if (stringBuffer.length() > 0) { stringBuffer.deleteCharAt(stringBuffer.length() - 1); moveCursor(cursorRow, cursorCol - 1); print(cursorRow, cursorCol, ' ', background, background); } break; case KeyEvent.CHAR_UNDEFINED: case KeyEvent.VK_DELETE: case KeyEvent.VK_ESCAPE: // ignore these characters break; default: if (stringBuffer.length() < maxLen) { stringBuffer.append(input); print(cursorRow, cursorCol, input, foreground, background); moveCursor(cursorRow, cursorCol + 1); } } } hideCursor(); render(); return stringBuffer.toString(); } @Override public void exit() { frame.dispose(); } @Override @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") public synchronized void keyPressed(final KeyEvent keyEvent) { lastKeyPressed = keyEvent; } @Override @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") public synchronized void keyTyped(final KeyEvent keyEvent) { lastKeyTyped = keyEvent; } @Override @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") public synchronized void keyReleased(final KeyEvent keyEvent) { /* do nothing */ } }