Package mikera.gui

Source Code of mikera.gui.JConsole

package mikera.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.io.PrintStream;
import java.util.Arrays;

import javax.swing.JComponent;
import javax.swing.Timer;

import mikera.gui.ConsoleData;

/**
* Class implementing a Swing-based text console
*
* Principles:
* - provides a fixed number of rows and columns, but can be resized
* - each cell can have its own foreground and background colour
* - The main font determines the grid size
*
* @author Mike Anderson
*
*/
public class JConsole extends JComponent implements HierarchyListener {
  private static final long serialVersionUID = 3571518591759968333L;

  private static final Color DEFAULT_FOREGROUND = Color.LIGHT_GRAY;
  private static final Color DEFAULT_BACKGROUND = Color.BLACK;
  private static final Font DEFAULT_FONT = new Font("Courier New", Font.PLAIN, 18);
  private static final int DEFAULT_BLINKRATE = 200;
  private static final boolean DEFAULT_BLINK_ON = true;
  private ConsoleData data = new ConsoleData();

  private int fontWidth;
  private int fontHeight;
  private int fontYOffset;

  private boolean cursorVisible = false;
  private boolean cursorBlinkOn = true;
  private boolean cursorInverted = true;

  private int cursorX = 0;
  private int cursorY = 0;
  private Font mainFont = null;
  private Font currentFont = null;
  private Color currentForeground = DEFAULT_FOREGROUND;
  private Color currentBackground = DEFAULT_BACKGROUND;

  private Timer blinkTimer;

  public JConsole(int columns, int rows) {
    setMainFont(DEFAULT_FONT);
    setFont(mainFont);
    init(columns, rows);
    if (DEFAULT_BLINK_ON) {
      setCursorBlink(true);
    }
  }

  /**
   * Sets the main font of the console, which is used to determine the size of
   * characters
   *
   * @param font
   */
  public void setMainFont(Font font) {
    mainFont = font;

    FontRenderContext fontRenderContext = new FontRenderContext(
        mainFont.getTransform(), false, false);
    Rectangle2D charBounds = mainFont.getStringBounds("X",
        fontRenderContext);
    fontWidth = (int) charBounds.getWidth();
    fontHeight = (int) charBounds.getHeight();
    fontYOffset = -(int) charBounds.getMinY();
   
    setPreferredSize(new Dimension(data.columns * fontWidth, data.rows * fontHeight));

    repaint();
  }

  /**
   * Utility class to handle the cursor blink animations
   */
  private class TimerAction implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
      if (cursorBlinkOn && isShowing()) {
        cursorInverted = !cursorInverted;
        repaintArea(getCursorX(), getCursorY(), 1, 1);
      } else {
        stopBlinking();
      }
    }
  }

  private void stopBlinking() {
    if (blinkTimer != null) {
      blinkTimer.stop();
      cursorInverted = true;
    }
  }

  private void startBlinking() {
    getTimer().start();
  }

  public void setCursorBlink(boolean blink) {
    if (blink) {
      cursorBlinkOn = true;
      startBlinking();
    } else {
      cursorBlinkOn = false;
      stopBlinking();
    }
  }

  public void setBlinkDelay(int millis) {
    getTimer().setDelay(millis);
  }

  private Timer getTimer() {
    if (blinkTimer == null) {
      blinkTimer = new Timer(DEFAULT_BLINKRATE, new TimerAction());
      blinkTimer.setRepeats(true);
      if (cursorBlinkOn) {
        startBlinking();
      }
    }
    return blinkTimer;
  }

  @Override
  public void addNotify() {
    super.addNotify();
    addHierarchyListener(this);
  }

  @Override
  public void removeNotify() {
    removeHierarchyListener(this);
    super.removeNotify();
  }

  public void setRows(int rows) {
    resize(this.data.columns, rows);
  }

  public void setFont(Font f) {
    currentFont = f;
  }

  public void setCursorVisible(boolean visible) {
    cursorVisible = visible;
  }

  public int getRows() {
    return data.rows;
  }

  public void setColumns(int columns) {
    resize(columns, this.data.rows);
  }

  public int getColumns() {
    return data.columns;
  }

  public int getFontWidth() {
    return fontWidth;
  }

  public int getFontHeight() {
    return fontHeight;
  }

  /**
   * Fires a repaint event on a specified rectangle of characters in the
   * console
   */
  public void repaintArea(int column, int row, int width, int height) {
    int fw = getFontWidth();
    int fh = getFontHeight();
    repaint(column * fw, row * fh, width * fw, height * fh);
  }

  /**
   * Initialises the console to a specified size
   */
  protected void init(int columns, int rows) {
    data.init(columns, rows);
    Arrays.fill(data.background, DEFAULT_BACKGROUND);
    Arrays.fill(data.foreground, DEFAULT_FOREGROUND);
    Arrays.fill(data.font, DEFAULT_FONT);
    Arrays.fill(data.text, ' ');

    setPreferredSize(new Dimension(columns * fontWidth, rows * fontHeight));
  }

  public void resize(int columns, int rows) {
    throw new UnsupportedOperationException();
  }

  public void clear() {
    clearArea(0, 0, data.columns, data.rows);
  }

  public void resetCursor() {
    repaintArea(cursorX, cursorY, 0, 0);
    cursorX = 0;
    cursorY = 0;
    repaintArea(cursorX, cursorY, 0, 0);
  }

  public void clearScreen() {
    clear();
    resetCursor();
  }

  private void clearArea(int column, int row, int width, int height) {
    data.fillArea(' ', currentForeground, currentBackground, currentFont,
        column, row, width, height);
    repaintArea(0, 0, width, height);
  }

  @Override
  public void paintComponent(Graphics graphics) {
    Graphics2D g = (Graphics2D) graphics;
    Rectangle r = g.getClipBounds();

    // AffineTransform textTransform=new AffineTransform();
    // textTransform.scale(fontWidth, fontHeight);
    // g.setTransform(textTransform);

    // calculate x and y range to redraw
    int x1 = (int) (r.getMinX() / fontWidth);
    int x2 = (int) (r.getMaxX() / fontWidth) + 1;
    int y1 = (int) (r.getMinY() / fontWidth);
    int y2 = (int) (r.getMaxY() / fontWidth) + 1;

    int curX = getCursorX();
    int curY = getCursorY();

    for (int j = Math.max(0, y1); j < Math.min(y2, data.rows); j++) {
      int offset = j * data.columns;
      int start = Math.max(x1, 0);
      int end = Math.min(x2, data.columns);

      while (start < end) {
        Color nfg = data.foreground[offset + start];
        Color nbg = data.background[offset + start];
        Font nf = data.font[offset + start];

        // index of ending position
        int i = start + 1;

        if ((j == curY) && (start == curX)) {
          if (cursorVisible && cursorBlinkOn && cursorInverted) {
            // swap foreground and background colours
            Color t = nfg;
            nfg = nbg;
            nbg = t;
          }
        } else {
          // detect run
          while ((i < end) && (!((j == curY) && (i == curX)))
              && (nfg == data.foreground[offset + i])
              && (nbg == data.background[offset + i])
              && (nf == data.font[offset + i])) {
            i++;
          }
        }

        // set font
        g.setFont(nf);

        // draw background
        g.setBackground(nbg);
        g.clearRect(fontWidth * start, j * fontHeight, fontWidth
            * (i - start), fontHeight);

        // draw chars up to this point
        g.setColor(nfg);
        for (int k=start; k<i; k++) {
          g.drawChars(data.text, offset + k, 1, k
              * fontWidth, j * fontHeight + fontYOffset);
        }
        start = i;
      }
    }
  }

  public void setCursorPos(int column, int row) {
    if ((column < 0) || (column >= data.columns))
      throw new Error("Invalid X cursor position: " + column);
    if ((row < 0) || (row >= data.rows))
      throw new Error("Invalid Y cursor position: " + row);
    cursorX = column;
    cursorY = row;
  }

  public int getCursorX() {
    return cursorX;
  }

  public int getCursorY() {
    return cursorY;
  }

  public void setForeground(Color c) {
    currentForeground = c;
  }

  public void setBackground(Color c) {
    currentBackground = c;
  }

  public Color getForeground() {
    return currentForeground;
  }

  public Color getBackground() {
    return currentBackground;
  }

  public char getCharAt(int column, int row) {
    return data.getCharAt(column, row);
  }

  public Color getForegroundAt(int column, int row) {
    return data.getForegroundAt(column, row);
  }

  public Color getBackgroundAt(int column, int row) {
    return data.getBackgroundAt(column, row);
  }

  public Font getFontAt(int column, int row) {
    return data.getFontAt(column, row);
  }

  /**
   * Redirects System.out to this console by calling System.setOut
   */
  public void captureStdOut() {
    PrintStream ps = new PrintStream(System.out) {
      public void println(String x) {
        writeln(x);
      }
    };

    System.setOut(ps);
  }

  public void write(char c) {
    data.setDataAt(cursorX, cursorY, c, currentForeground,
        currentBackground, currentFont);
    moveCursor(c);
  }

  private void moveCursor(char c) {
    switch (c) {
    case '\n':
      cursorY++;
      cursorX = 0;
      break;
    default:
      cursorX++;
      if (cursorX >= data.columns) {
        cursorX = 0;
        cursorY++;
      }
      break;
    }
  }

  public void writeln(String line) {
    write(line);
    write('\n');
  }

  public void write(String string, Color foreGround, Color backGround) {
    Color foreTemp = currentForeground;
    Color backTemp = currentBackground;
    setForeground(foreGround);
    setBackground(backGround);
    write(string);
    setForeground(foreTemp);
    setBackground(backTemp);
  }

  public void write(String string) {
    for (int i = 0; i < string.length(); i++) {
      char c = string.charAt(i);
      write(c);
    }
  }

  public void fillArea(char c, Color fg, Color bg, int column, int row,
      int width, int height) {
    data.fillArea(c, fg, bg, currentFont, column, row, width, height);
    repaintArea(column, row, width, height);
  }

  @Override
  public void hierarchyChanged(HierarchyEvent e) {
    if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
      if (isShowing()) {
        startBlinking();
      } else {
        stopBlinking();
      }
    }
  }

}
TOP

Related Classes of mikera.gui.JConsole

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.