/* * $Id: Viewport6.java 557 2008-04-17 07:13:54Z weiju $ * * Created on 2006/02/23 * Copyright 2005-2008 by Wei-ju Wu * This file is part of The Z-machine Preservation Project (ZMPP). * * ZMPP 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. * * ZMPP 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 ZMPP. If not, see <http://www.gnu.org/licenses/>. */ package org.zmpp.swingui; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.Graphics; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import javax.swing.JComponent; import org.zmpp.base.Memory; import org.zmpp.encoding.ZsciiString; import org.zmpp.io.OutputStream; import org.zmpp.vm.Machine; import org.zmpp.vm.ScreenModel; import org.zmpp.vm.ScreenModel6; import org.zmpp.vm.StoryFileHeader; import org.zmpp.vm.TextCursor; import org.zmpp.vm.Window6; import org.zmpp.vm.StoryFileHeader.Attribute; public class Viewport6 extends JComponent implements ScreenModel6, Viewport { private static final long serialVersionUID = 1L; private static final int NUM_V6_WINDOWS = 8; private Canvas canvas; private BufferedImage imageBuffer; private FontFactory fontFactory; private Font fixedFont, standardFont; private boolean initialized; private int defaultBackground, defaultForeground; private Window6Impl[] windows; private int currentwindow; private Machine machine; private LineEditor editor; private OutputStream outputstream; private DisplaySettings settings; public Viewport6(Machine machine, LineEditor editor, DisplaySettings settings) { this.machine = machine; this.editor = editor; this.fontFactory = new FontFactory(); this.settings = settings; windows = new Window6Impl[NUM_V6_WINDOWS]; outputstream = new ScreenOutputStream(machine, this); } public CursorWindow getCurrentWindow() { return windows[currentwindow]; } public LineEditor getLineEditor() { return editor; } public int getDefaultBackground() { return defaultBackground; } public int getDefaultForeground() { return defaultForeground; } public Canvas getCanvas() { return canvas; } public Machine getMachine() { return machine; } // ******************************************************************** // ***** ScreenModel interface // ******************************************* public void eraseWindow(int window) { if (window == - 1 || window == -2) { // We need to have special handling for the two different cases Color erasecolor = ColorTranslator.getInstance().translate(defaultBackground); canvas.fillRect(erasecolor, 0, 0, getWidth(), getHeight()); } else { windows[window].clear(); } } public void eraseLine(int value) { windows[currentwindow].eraseLine(value); } public TextCursor getTextCursor() { return windows[currentwindow].getCursor(); } public void setTextCursor(int line, int column, int window) { int w = (window == ScreenModel.CURRENT_WINDOW) ? currentwindow : window; //System.out.printf("@set_cursor w: %d l: %d c: %d\n", w, line, column); windows[w].getCursor().setPosition(line, column); } public void splitWindow(int linesUpperWindow) { windows[1].resize(linesUpperWindow); int heightWindowTop = windows[1].getHeight(); windows[0].setVerticalBounds(heightWindowTop + 1, getHeight() - heightWindowTop); } public void setWindow(int window) { getOutputStream().flush(); currentwindow = window; } public void setTextStyle(int style) { getOutputStream().flush(); windows[currentwindow].setTextStyle(style); } public void setBufferMode(boolean flag) { windows[currentwindow].setBufferMode(flag); } public void setPaging(boolean flag) { // TODO //System.out.println("setPaging()"); } /** * Reset the line counters. */ public void resetPagers() { // TODO //System.out.println("resetPagers()"); } public synchronized void waitInitialized() { while (!isInitialized()) { try { wait(); } catch (Exception ex) { } } } public synchronized boolean isInitialized() { return initialized; } public synchronized void setInitialized() { this.initialized = true; notifyAll(); } public OutputStream getOutputStream() { return outputstream; } /** * {@inheritDoc} */ public void setForegroundColor(int colornum, int window) { //System.out.printf("@set_foreground %d %d\n", colornum, window); if (colornum > 0) { getOutputStream().flush(); int w = (window == CURRENT_WINDOW) ? currentwindow : window; windows[w].setForeground(colornum); } } /** * {@inheritDoc} */ public void setBackgroundColor(int colornum, int window) { //System.out.printf("@set_background %d %d\n", colornum, window); if (colornum > 0) { getOutputStream().flush(); int w = (window == CURRENT_WINDOW) ? currentwindow : window; windows[w].setBackground(colornum); } } public void redraw() { repaintInUiThread(); } public int setFont(int fontnum) { getOutputStream().flush(); return windows[currentwindow].setFont(fontnum); } /** * {@inheritDoc} */ public synchronized void displayCursor(boolean showCaret) { windows[currentwindow].displayCursor(showCaret); } /** * {@inheritDoc} */ public void reset() { setScreenProperties(); } // ******************************************************************** // ***** ScreenModel V6 interface // ******************************************* /** * {@inheritDoc} */ public void setMouseWindow(int window) { // do nothing at the moment //System.out.println("setMouseWindow(): " + window); } /** * {@inheritDoc} */ public Window6 getSelectedWindow() { return windows[currentwindow]; } /** * {@inheritDoc} */ public Window6 getWindow(int window) { return windows[window]; } /** * {@inheritDoc} */ public void setTextWidthInUnits(char[] zchars) { ZsciiString str = new ZsciiString(zchars); int textwidth = canvas.getStringWidth(windows[currentwindow].getFont(), str.toString()); machine.getGameData().getStoryFileHeader().setOutputStream3TextWidth(textwidth); } /** * {@inheritDoc} */ public void readMouse(int array) { MouseEvent event = editor.getLastMouseEvent(); Memory memory = machine.getGameData().getMemory(); int buttonmask = 0; if ((event.getButton() & MouseEvent.BUTTON1) != 0) { buttonmask |= 1; } if ((event.getButton() & MouseEvent.BUTTON2) != 0) { buttonmask |= 2; } if ((event.getButton() & MouseEvent.BUTTON3) != 0) { buttonmask |= 4; } memory.writeUnsignedShort(array, event.getY() + 1); memory.writeUnsignedShort(array + 2, event.getX() + 1); memory.writeUnsignedShort(array + 4, buttonmask); // TODO: Menu items } // ******************************************************************** // ***** Component functions // ******************************************* /** * {@inheritDoc} */ @Override public void paintComponent(Graphics g) { if (imageBuffer == null) { imageBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); // Default colors defaultBackground = settings.getDefaultBackground(); defaultForeground = settings.getDefaultForeground(); canvas = new CanvasImpl(imageBuffer, this, settings.getAntialias()); for (int i = 0; i < NUM_V6_WINDOWS; i++) { windows[i] = new Window6Impl(this, fontFactory, i); } // Set initial window sizes windows[0].setSize(getHeight(), getWidth()); windows[1].setSize(0, getWidth()); Graphics g_img = imageBuffer.getGraphics(); Color bgcolor = ColorTranslator.getInstance().translate(defaultBackground); g_img.setColor(bgcolor); g_img.fillRect(0, 0, getWidth(), getHeight()); setScreenProperties(); // Set the final default settings to each window for (int i = 0; i < NUM_V6_WINDOWS; i++) { windows[i].setFont(1); windows[i].setBackground(defaultBackground); windows[i].setForeground(defaultForeground); } setInitialized(); } g.drawImage(imageBuffer, 0, 0, this); } // ******************************************************************** // ***** Private functions // ******************************************* /** * Sets the screen properties to the display. */ private void setScreenProperties() { StoryFileHeader fileheader = machine.getGameData().getStoryFileHeader(); fileheader.setEnabled(Attribute.SUPPORTS_BOLD, true); fileheader.setEnabled(Attribute.SUPPORTS_FIXED_FONT, true); fileheader.setEnabled(Attribute.SUPPORTS_ITALIC, true); fileheader.setEnabled(Attribute.SUPPORTS_COLOURS, true); fileheader.setDefaultBackgroundColor(defaultBackground); fileheader.setDefaultForegroundColor(defaultForeground); determineStandardFont(); fileheader.setFontWidth(canvas.getCharWidth(fixedFont, '0')); fileheader.setFontHeight(canvas.getFontHeight(fixedFont)); updateDimensionsInHeader(); } /** * Determines the standard fonts for display. */ private void determineStandardFont() { standardFont = new Font("Monospaced", Font.ROMAN_BASELINE, 12); fixedFont = standardFont; fontFactory.initialize(standardFont, fixedFont); } /** * Sets the display dimensions into the story file header. */ private void updateDimensionsInHeader() { StoryFileHeader fileheader = machine.getGameData().getStoryFileHeader(); int numcols = imageBuffer.getWidth() / canvas.getCharWidth(fixedFont, '0'); int numlines = imageBuffer.getHeight() / canvas.getFontHeight(fixedFont); fileheader.setScreenWidth(numcols); fileheader.setScreenHeight(numlines); fileheader.setScreenWidthUnits(imageBuffer.getWidth()); fileheader.setScreenHeightUnits(imageBuffer.getHeight()); } /** * Synchronizes the repaint action to the UI thread. */ private void repaintInUiThread() { try { EventQueue.invokeAndWait(new Runnable() { public void run() { // replace the expensive repaint() call with a fast copying of // the double buffer if (imageBuffer != null) { getGraphics().drawImage(imageBuffer, 0, 0, Viewport6.this); } } }); } catch (Exception ex) { ex.printStackTrace(); } } }