/***************************************************************************** * Copyright (c) 2006, 2007 g-Eclipse Consortium * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Thomas Koeckerbauer GUP, JKU - initial API and implementation *****************************************************************************/ package eu.geclipse.terminal.internal; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; class TerminalPainter implements PaintListener { private Terminal terminal; private TerminalSelection selection; private Char startChar = null; private int startCharLine = 0; private int startCharCol = 0; private boolean selected; private StringBuilder buffer = new StringBuilder(); private int numChars = 0; private LineHeightMode lineHeight; private LineWidthMode lineWidth; private LineHeightMode[] lineHeightMode; private LineWidthMode[] lineWidthMode; private Font normalFont; private Font boldFont; private Font normalDoubleFont; private Font boldDoubleFont; // Character a - z of the VT100 "special" character set private final char[] specialChars = { // TODO find missing characters in unicode table '\u2591', // a '\u2409', // b '\u240c', // c '\u240d', // d '\u240a', // e '\u00b0', // f '\u00b1', // g '?', // h '?', // i '\u2518', // j '\u2510', // k '\u250c', // l '\u2514', // m '\u253c', // n '?', // o '?', // p '\u2500', // q '?', // r '?', // s '\u251c', // t '\u2524', // u '\u2534', // v '\u252c', // w '\u2502', // x '\u2264', // y '\u2265' // z }; TerminalPainter( final Terminal term ) { this.terminal = term; } private void addToStringBuffer( final StringBuilder buf, final Char ch ) { if ( ch.charSet == CharSet.SPECIAL && ch.ch >= 'a' && ch.ch <= 'z' ) { buf.append( this.specialChars[ ch.ch - 'a' ] ); } else { buf.append( ch.ch ); } } private void paintBufferedArea( final GC gc ) { int fontHeight = this.terminal.getFontHeigth(); int fontWidth = this.terminal.getFontWidth(); if ( this.lineWidth == LineWidthMode.DOUBLE ) fontWidth *= 2; if ( this.startChar.negative ^ this.terminal.isInReverseScreenMode() ^ this.selected ) { gc.setBackground( this.startChar.fgColor ); gc.setForeground( this.startChar.bgColor ); } else { gc.setBackground( this.startChar.bgColor ); gc.setForeground( this.startChar.fgColor ); } if ( this.startChar.ch == 0 ) { gc.fillRectangle( fontWidth * this.startCharCol, fontHeight * this.startCharLine, fontWidth * this.numChars, fontHeight ); } else { // XXX fillRectangle is a hack against ugly fonts gc.fillRectangle( fontWidth * this.startCharCol, fontHeight * this.startCharLine, fontWidth * this.numChars, fontHeight ); if ( this.lineHeight != LineHeightMode.NORMAL && this.lineWidth == LineWidthMode.DOUBLE ) { Rectangle rect = gc.getClipping(); gc.setClipping( 0, fontHeight * this.startCharLine, rect.width, fontHeight ); if ( this.startChar.bold ) gc.setFont( this.boldDoubleFont ); else gc.setFont( this.normalDoubleFont ); } else { gc.setClipping( (Rectangle) null ); if ( this.startChar.bold ) gc.setFont( this.boldFont ); else gc.setFont( this.normalFont ); } if ( this.lineWidth == LineWidthMode.NORMAL ) { gc.drawText( this.buffer.toString(), fontWidth * this.startCharCol, fontHeight * this.startCharLine ); } else { int heightOffset = 0; if ( this.lineHeight == LineHeightMode.DOUBLE_BOTTOM ) { heightOffset = -1; } String string = this.buffer.toString(); for ( int i = 0; i < string.length(); i++ ) { gc.drawText( string.substring( i, i + 1 ), fontWidth * ( this.startCharCol + i ), fontHeight * ( this.startCharLine + heightOffset ) ); } } if ( this.startChar.underscore ) { for ( int col = this.startCharCol; col < this.startCharCol + this.buffer.length(); col++ ) { gc.drawText( "_", fontWidth * col, //$NON-NLS-1$ fontHeight * this.startCharLine, true ); } } } } private void resetStartChar( final Char ch, final int line, final int col ) { if ( ch.ch != 0 ) { this.buffer.delete( 0, this.buffer.length() ); addToStringBuffer( this.buffer, ch ); } this.startChar = ch; this.startCharLine = line; this.startCharCol = col; this.selected = this.selection.isSelected( line + this.terminal.getScrollbarPosLine(), col ); this.numChars = 1; this.lineHeight = this.lineHeightMode[ line + this.terminal.getScrollbarPosLine() ]; this.lineWidth = this.lineWidthMode[ line + this.terminal.getScrollbarPosLine() ]; } /* (non-Javadoc) * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent) */ public void paintControl( final PaintEvent paintEvent ) { Char[][] screenBuffer = this.terminal.getScreenBuffer(); this.lineHeightMode = this.terminal.getLineHeightMode(); this.lineWidthMode = this.terminal.getLineWidthMode(); this.selection = (TerminalSelection) this.terminal.getSelection(); int numLines = this.terminal.getNumLines(); int numCols = screenBuffer[0].length; int fontHeight = this.terminal.getFontHeigth(); int fontWidth = this.terminal.getFontWidth(); int startLine = paintEvent.y / fontHeight; int endLine = ( paintEvent.y + paintEvent.height + fontHeight ) / fontHeight; boolean fillBottomGap = screenBuffer.length < endLine + this.terminal.getScrollbarPosLine(); if ( endLine > numLines && fillBottomGap) endLine = numLines; for ( int line = startLine; line < endLine; line++ ) { int startCol = paintEvent.x / fontWidth; int endCol = ( paintEvent.x + paintEvent.width + fontWidth ) / fontWidth; if ( endCol > numCols ) endCol = numCols; if ( this.lineWidthMode[ line + this.terminal.getScrollbarPosLine() ] == LineWidthMode.DOUBLE ) { startCol /= 2; endCol = ( endCol + 1 ) / 2; } for ( int col = startCol; col < endCol; col++ ) { Char ch = screenBuffer[ line + this.terminal.getScrollbarPosLine() ][ col ]; if ( this.startChar == null ) { resetStartChar( ch, line, col ); } else if ( this.startChar.ch == 0 && ch.ch == 0 && ch.hasSameFormat( this.startChar ) && this.selection.isSelected( line + this.terminal.getScrollbarPosLine(), col ) == this.selected ) { this.numChars++; } else if ( this.startChar.ch != 0 && ch.ch != 0 && ch.hasSameFormat( this.startChar ) && this.selection.isSelected( line + this.terminal.getScrollbarPosLine(), col ) == this.selected ) { addToStringBuffer( this.buffer, ch ); this.numChars++; } else { paintBufferedArea( paintEvent.gc ); resetStartChar( ch, line, col ); } } if ( this.startChar != null ) { paintBufferedArea( paintEvent.gc ); this.startChar = null; } if ( line + this.terminal.getScrollbarPosLine() == this.terminal.getCursorLine() + this.terminal.getHistorySize() ) { int widthMult = 1; if ( this.lineWidthMode[ line + this.terminal.getScrollbarPosLine() ] == LineWidthMode.DOUBLE ) widthMult = 2; // draw cursor paintEvent.gc.drawText( "_", //$NON-NLS-1$ fontWidth * this.terminal.getCursorCol() * widthMult, fontHeight * line , true ); } } // fill the gap at the widget border paintEvent.gc.setBackground( this.terminal.getBackground() ); if ( fillBottomGap ) { if ( ( paintEvent.y + paintEvent.height ) >= ( fontHeight * numLines ) ) { int height = ( paintEvent.y + paintEvent.height ) - ( fontHeight * numLines ); paintEvent.gc.fillRectangle( paintEvent.x, fontHeight * numLines, paintEvent.width, height ); } } if ( ( paintEvent.x + paintEvent.width ) >= ( fontWidth * numCols ) ) { int width = ( paintEvent.x + paintEvent.width ) - ( fontWidth * numCols ); paintEvent.gc.fillRectangle( fontWidth * numCols, paintEvent.y, width, paintEvent.height ); } } void setFont( final Font font ) { FontData fontData = font.getFontData()[0]; fontData.setStyle( SWT.NORMAL ); this.normalFont = new Font( font.getDevice(), fontData ); fontData.setStyle( SWT.BOLD ); this.boldFont = new Font( font.getDevice(), fontData ); fontData.setStyle( SWT.NORMAL ); fontData.setHeight( fontData.getHeight() * 2 ); this.normalDoubleFont = new Font( font.getDevice(), fontData ); fontData.setStyle( SWT.BOLD ); this.boldDoubleFont = new Font( font.getDevice(), fontData ); } void scrollUp( final int topMargin, final int bottomMargin ) { Char[][] screenBuffer = this.terminal.getScreenBuffer(); int numCols = screenBuffer[0].length; int fontHeight = this.terminal.getFontHeigth(); int fontWidth = this.terminal.getFontWidth(); GC gc = new GC( this.terminal ); gc.copyArea( 0, fontHeight * ( topMargin + this.terminal.getHistorySize() - this.terminal.getScrollbarPosLine() + 1 ), fontWidth * numCols, fontHeight * ( bottomMargin - topMargin ), 0, fontHeight * ( topMargin + this.terminal.getHistorySize() - this.terminal.getScrollbarPosLine() ), true ); gc.dispose(); } void scrollDown( final int topMargin, final int bottomMargin ) { Char[][] screenBuffer = this.terminal.getScreenBuffer(); int numCols = screenBuffer[0].length; int fontHeight = this.terminal.getFontHeigth(); int fontWidth = this.terminal.getFontWidth(); GC gc = new GC( this.terminal ); gc.copyArea( 0, fontHeight * ( topMargin + this.terminal.getHistorySize() - this.terminal.getScrollbarPosLine() ), fontWidth * numCols, fontHeight * ( bottomMargin - topMargin ), 0, fontHeight * ( topMargin + this.terminal.getHistorySize() - this.terminal.getScrollbarPosLine() + 1 ), true ); gc.dispose(); } }