/************************************************************************** * Copyright (c) 2001, 2002, 2003 by Acunia N.V. All rights reserved. * * * * This software is copyrighted by and is the sole property of Acunia N.V. * * and its licensors, if any. All rights, title, ownership, or other * * interests in the software remain the property of Acunia N.V. and its * * licensors, if any. * * * * This software may only be used in accordance with the corresponding * * license agreement. Any unauthorized use, duplication, transmission, * * distribution or disclosure of this software is expressly forbidden. * * * * This Copyright notice may not be removed or modified without prior * * written consent of Acunia N.V. * * * * Acunia N.V. reserves the right to modify this software without notice. * * * * Acunia N.V. * * Philips-site 5, box 3 info@acunia.com * * 3001 Leuven http://www.acunia.com * * Belgium - EUROPE * **************************************************************************/ package com.acunia.wonka.rudolph.peers; import java.awt.peer.*; import java.awt.event.*; import java.awt.*; public class DefaultTextArea extends DefaultTextComponent implements TextAreaPeer, MouseListener, MouseMotionListener, FocusListener { // border scrolling area private final static int TEXTSCROLL_STOP = -1; private final static int TEXTSCROLL_UP = 1; private final static int TEXTSCROLL_DOWN = 2; private final static int TEXTSCROLL_LEFT = 3; private final static int TEXTSCROLL_RIGHT = 4; //Painters private HAreaScrollPainter hScroll; private VAreaScrollPainter vScroll; private boolean vScrollVisible; private TextAreaPainter textPainter; //colors private Color[] textAreaColors; //screen dimensions private Dimension textScreen; private Dimension totalScreen; //last mouse position and mouse event runner private Point lastMousePosition; final static ScrollRunner mouseEventThread = new ScrollRunner(); // painting buffer private Image backgroundImage; private Graphics backgroundGraphics; // selective painting flags private boolean repaintText; private boolean repaintHScroll; private boolean repaintVScroll; //protected Rectangle cursorData; //PointData cursorData; protected int cursorLine; protected int cursorBufferOffset; protected int cursorHorizontalOffset; //protected Rectangle selStartData;//PointData selStartData; protected int startLine; protected int startBufferOffset; protected int startHorizontalOffset; //protected Rectangle selStopData;//PointData selStopData; protected int stopLine; protected int stopBufferOffset; protected int stopHorizontalOffset; private boolean initialized = false; private int[] tempData; //private Rectangle tempData; //PointData tempData; public DefaultTextArea(TextArea textArea) { super(textArea); } private synchronized void init() { initialized = true; position = 0; cursorLine = -1; cursorBufferOffset = -1; cursorHorizontalOffset = -1; selectionStart = 0; startLine = -1; startBufferOffset = -1; startHorizontalOffset = -1; selectionStop = 0; stopLine = -1; stopBufferOffset = -1; stopHorizontalOffset = -1; //tempData= new Rectangle(); //new PointData(); tempData=new int[4]; // see notes on the TextAreaPainter.setScreenPosition(int[]) format //colors & font textAreaColors = RudolphPeer.getBarColors(); // previously super.setFont(RudolphTextAreaPeer.DEFAULT_FONT); //initialise the dimensions textScreen = new Dimension(); totalScreen = new Dimension(); //vertical scrollbar (we ALWAYS use one to enable vertical text scrolling vScroll = new VAreaScrollPainter(); int scrollbarVis = ((TextArea)component).getScrollbarVisibility(); //According to display type, assign text painter, horizontal scrollbar and vertical scrollbar visibility if(scrollbarVis == TextArea.SCROLLBARS_NONE) { //no scrollbars (vertical bar is present but not visible) hScroll = null; vScrollVisible = false; textPainter = TextAreaPainter.getNewPainter(((TextArea)component).getFont(),text,textAreaColors,textScreen,false); } else if(scrollbarVis == TextArea.SCROLLBARS_HORIZONTAL_ONLY) { // only horizontal scrollbar (vertical is present but not visible) hScroll = new HAreaScrollPainter(); hScroll.setBarColors(textAreaColors); vScrollVisible = false; textPainter = TextAreaPainter.getNewPainter(((TextArea)component).getFont(),text,textAreaColors,textScreen,true); hScroll.setLineStep(textPainter.getMaxAdvance()); } else if(scrollbarVis == TextArea.SCROLLBARS_VERTICAL_ONLY) { // only vertical scrollbar hScroll = null; vScrollVisible = true; vScroll.setBarColors(textAreaColors); textPainter = TextAreaPainter.getNewPainter(((TextArea)component).getFont(),text,textAreaColors,textScreen,false); } else { // if(scrollbarVis == SCROLLBARS_BOTH) // if not defined otherwise, the textarea ALWAYS shows horizontal AND vertical bars hScroll = new HAreaScrollPainter(); hScroll.setBarColors(textAreaColors); vScrollVisible = true; vScroll.setBarColors(textAreaColors); textPainter = TextAreaPainter.getNewPainter(((TextArea)component).getFont(),text,textAreaColors,textScreen,true); hScroll.setLineStep(textPainter.getMaxAdvance()); } //add mouse listeners ((TextArea)component).addMouseListener(this); ((TextArea)component).addMouseMotionListener(this); //If version with keyboard, also add key listener //addKeyListener(this); //last mouse click point lastMousePosition = new Point(0,0); // painting buffer backgroundImage = null; backgroundGraphics = null; //no repainting untill screen calculations and screen buffer repaintText = false; repaintHScroll = false; repaintVScroll = false; ((TextArea) component).addFocusListener(this); } public Dimension getMinimumSize(int rows, int cols) { if(!initialized) init(); return getPreferredSize(rows, cols); } public Dimension getPreferredSize(int rows, int cols) { if(!initialized) init(); Dimension size = textPainter.getScreenSize(cols, rows); if(hScroll != null) { size.height +=RudolphScrollbarPeer.HSCROLL_HEIGHT; } if(vScrollVisible) { size.width += RudolphScrollbarPeer.VSCROLL_WIDTH; } return size; } /* ** Insert desired String to into the current text at the desired position and show this new text in TextArea window ** (This also fires a TextEvent to the textListener to indicate the test has changed) ** @note: Java 1.2 specifications remains wonderfully vague about what happens when the insert position is out of range. ** @note: here we'll throw an IllegalArgumentexception with some diagnostics */ public synchronized void insert(String newtext, int pos) { if(!initialized) init(); //security on position if(updatePosition(pos)) { //okay, collapse selection to position selectionStart = position; //updateStart(pos); selectionStop = position; //updateStop(pos); // (when Selectionstart == selectionStop, startLine, stopLine and the likes are never asked, so never bother assigning them) // insert new text (as all changes to the text happen AFTER pos, its value remains unchanged) text = new String(new StringBuffer(text).insert(pos,newtext)); // text to painter and recalculate sizes and scrollbars textPainter.setImage(text); if(hScroll != null) { hScroll.setBarRange(textPainter.getMaximumWidth()); } vScroll.setBarRange(textPainter.getMaximumLines()); //tell the text listeners fireTextEvent(); // show the changes repaintText = true; repaintHScroll = true; repaintVScroll = true; paint(getGraphics()); } else { // not in range // throw new IllegalArgumentException("TextArea.insert(): position "+pos+" not in range of current text ("+text.length()+" chars)"); } } /* ** replace the desired section of the current text with the given String and show this new text in TextArea window ** (This also fires a TextEvent to the textListener to indicate the test has changed) ** @note: Java 1.2 specifications remains wonderfully vague about what happens when the insert position is out of range. ** @note: or the start position is bigger then the end position. here we'll throw an IllegalArgumentexception with some diagnostics */ public synchronized void replaceRange(String newtext, int start, int stop) { if(!initialized) init(); //boundaries check if(start>stop) { // throw new IllegalArgumentException("TextArea.ReplaceRange(): end position "+stop+" comes before start position "+start); return; } else if(start<0 || start>text.length() || stop<0 || stop>text.length()) { // throw new IllegalArgumentException("TextArea.ReplaceRange(): selection("+start+","+stop+") not in range of current text (0, "+text.length()+")"); return; } //else //set new text text = new String(new StringBuffer(text).replace(start,stop,newtext)); // text to painter and recalculate sizes and scrollbars textPainter.setImage(text); if(hScroll != null) { hScroll.setBarRange(textPainter.getMaximumWidth()); } vScroll.setBarRange(textPainter.getMaximumLines()); // set new cursor to end of new text, collapse selection to cursor updatePosition(start+newtext.length()); selectionStart = position; //updateStart(pos); selectionStop = position; //updateStop(pos); //tell the text listeners fireTextEvent(); // show the changes repaintText = true; repaintHScroll = true; repaintVScroll = true; paint(getGraphics()); } /* ** Deprecated */ public void insertText(String text, int pos) { } public Dimension minimumSize(int rows, int cols) { return null; } public Dimension preferredSize(int rows, int cols) { return null; } public void replaceText(String str, int start, int end) { } /* ** KeyEvents */ public void keyPressed(KeyEvent evt) { boolean imagechanged = false; switch(evt.getKeyCode()) { // key left: move carret left and set selection to carer case KeyEvent.VK_LEFT: if(evt.isShiftDown()) { // we want a selection textPainter.setScreenPosition(position-1, tempData); imagechanged = setNewSelection(tempData); } else { // cursor to position-1, collapse selection to cursor imagechanged = updatePosition(position-1); selectionStart = position; selectionStop = position; } break; // key right: move carret right and set selection to caret case KeyEvent.VK_RIGHT: if(evt.isShiftDown()) { // we want a selection textPainter.setScreenPosition(position+1, tempData); imagechanged = setNewSelection(tempData); } else { // cursor to position-1, collapse selection to cursor imagechanged = updatePosition(position+1); selectionStart = position; selectionStop = position; } break; // key home: move carret completely left case KeyEvent.VK_HOME: if(evt.isShiftDown()) { textPainter.setScreenPositionLine(cursorLine, 0, tempData); imagechanged = setNewSelection(tempData); } else { textPainter.setScreenPositionLine(cursorLine, 0, tempData); imagechanged = updateCursor(tempData); } break; case KeyEvent.VK_END: if(evt.isShiftDown()) { textPainter.setScreenPositionLine(cursorLine, -1, tempData); imagechanged = setNewSelection(tempData); } else { textPainter.setScreenPositionLine(cursorLine, -1, tempData); imagechanged = updateCursor(tempData); } break; case KeyEvent.VK_UP: if(evt.isShiftDown()) { textPainter.setScreenPositionLine(cursorLine-1, cursorHorizontalOffset+1, tempData); imagechanged = setNewSelection(tempData); } else { textPainter.setScreenPositionLine(cursorLine-1, cursorHorizontalOffset+1, tempData); imagechanged = updateCursor(tempData); } break; case KeyEvent.VK_DOWN: if(evt.isShiftDown()) { textPainter.setScreenPositionLine(cursorLine+1, cursorHorizontalOffset+1, tempData); imagechanged = setNewSelection(tempData); } else { textPainter.setScreenPositionLine(cursorLine+1, cursorHorizontalOffset+1, tempData); imagechanged = updateCursor(tempData); } break; case KeyEvent.VK_PAGE_UP: if(evt.isShiftDown()) { textPainter.setScreenPositionLine(cursorLine-textPainter.getViewportLines(), cursorHorizontalOffset+1, tempData); imagechanged = setNewSelection(tempData); } else { textPainter.setScreenPositionLine(cursorLine-textPainter.getViewportLines(), cursorHorizontalOffset+1, tempData); imagechanged = updateCursor(tempData); } break; case KeyEvent.VK_PAGE_DOWN: if(evt.isShiftDown()) { textPainter.setScreenPositionLine(cursorLine+textPainter.getViewportLines(), cursorHorizontalOffset+1, tempData); imagechanged = setNewSelection(tempData); } else { textPainter.setScreenPositionLine(cursorLine+textPainter.getViewportLines(), cursorHorizontalOffset+1, tempData); imagechanged = updateCursor(tempData); } break; } if(imagechanged) { // adjust viewport if needed repaintVScroll = adjustVerticalViewport(cursorLine); if(hScroll != null) { repaintHScroll = adjustHorizontalViewport(cursorHorizontalOffset); } //repaint text repaintText = true; paint(getGraphics()); } } public void keyReleased(KeyEvent evt) { } public void keyTyped(KeyEvent evt) { boolean imagechanged = false; wonka.vm.Etc.woempa(9, "DefaultTextArea.keyTyped: "+evt.toString()); switch(evt.getKeyCode()) { case KeyEvent.VK_BACK_SPACE: if(selectionStart < selectionStop ){ //deleteSelection(); text = new String(new StringBuffer(text).delete(selectionStart, selectionStop)); textPainter.setImage(text); updatePosition(selectionStart); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } else if(position>=1 && position<=text.length()) { text = new String(new StringBuffer(text).deleteCharAt(position-1)); textPainter.setImage(text); updatePosition(position-1); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } break; case KeyEvent.VK_DELETE: if(selectionStart < selectionStop ){ //deleteSelection(); text = new String(new StringBuffer(text).delete(selectionStart, selectionStop)); textPainter.setImage(text); updatePosition(selectionStart); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } else if(position>=0 && position<text.length()) { text = new String(new StringBuffer(text).deleteCharAt(position)); textPainter.setImage(text); updatePosition(position); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } break; case KeyEvent.VK_SHIFT: case KeyEvent.VK_ALT: case KeyEvent.VK_CONTROL: case KeyEvent.VK_TAB: case KeyEvent.VK_UNDEFINED: break; case KeyEvent.VK_ENTER: //special case: map the enter to a '/n' line break if(selectionStart < selectionStop ){ StringBuffer buf =new StringBuffer(text); buf.delete(selectionStart, selectionStop); buf.insert(selectionStart, '\n' ); text = new String(buf); textPainter.setImage(text); updatePosition(selectionStart+1); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } else if(position>=0 && position<=text.length() ) { text = new String(new StringBuffer(text).insert(position, '\n') ); textPainter.setImage(text); updatePosition(position+1); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } break; default: wonka.vm.Etc.woempa(9, "DefaultTextArea.keyTyped:default char: "+evt.getKeyChar()+" position: "+position); if(selectionStart < selectionStop ){ StringBuffer buf =new StringBuffer(text); buf.delete(selectionStart, selectionStop); buf.insert(selectionStart, evt.getKeyChar() ); text = new String(buf); textPainter.setImage(text); updatePosition(selectionStart+1); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } else if(position>=0 && position<=text.length() ) { text = new String(new StringBuffer(text).insert(position, evt.getKeyChar()) ); textPainter.setImage(text); updatePosition(position+1); selectionStart = position; selectionStop = position; imagechanged = true; fireTextEvent(); } } if(imagechanged) { // adjust viewport if needed repaintVScroll = adjustVerticalViewport(cursorLine); if(hScroll != null) { repaintHScroll = adjustHorizontalViewport(cursorHorizontalOffset); } //repaint text repaintText = true; paint(getGraphics()); } } /* ** Extends the Component's setColor function to pass the colors further to the scrollPainter */ public void setBackground(Color c) { if(!initialized) init(); // make new colors and tell them to the textarea and scrollbar painters textAreaColors = RudolphPeer.getBarColors(c, component.getForeground()); if(hScroll != null) { hScroll.setBarColors(textAreaColors); } vScroll.setBarColors(textAreaColors); textPainter.setTextColors(textAreaColors); // repaint all in new colors repaintText = true; repaintHScroll = true; repaintVScroll = true; if (component.isVisible()) { paint(getGraphics()); } super.setBackground(c); // component } public void setForeground(Color c) { // make new colors and tell them to the textarea and scrollbar painters textAreaColors[4] = c; if(hScroll != null) { hScroll.setBarColors(textAreaColors); } vScroll.setBarColors(textAreaColors); textPainter.setTextColors(textAreaColors); // repaint all in new colors repaintText = true; repaintHScroll = true; repaintVScroll = true; paint(getGraphics()); super.setForeground(c); // component } /* ** Extends the Component's setFont function to pass the desired font towards the scrollPainter (and from there to the actual painting ** algorithm in the RudolphTextAreaPeer) ** @remark though you can specify a font, at present the text is ALWAYS drawn in RudolphTextAreaPeer.DEFAULT_FONT for more speed ** @remark However, the font facility is already implemented here for later use */ public void setFont(Font f) { if(!initialized) init(); // tell new font to the text painter textPainter = TextAreaPainter.getNewPainter(f,text,textAreaColors,textScreen,(hScroll!=null)); // the font also changed the texts dimensions and viewports if(hScroll != null) { hScroll.setLineStep(textPainter.getMaxAdvance()); hScroll.setScreenWidth(textScreen.width, textPainter.getViewportWidth(), textPainter.getMaximumWidth()); textPainter.setTextOffset(hScroll.getBarPos() ); } vScroll.setScreenHeight( textScreen.height, textPainter.getViewportLines(),textPainter.getMaximumLines()); textPainter.setLineOffset(vScroll.getBarPos() ); //also recalculate PointData for position and selection(line wrapping might hace changed their lines) updatePosition(position); updateStart(selectionStart); updateStop(selectionStop); // repaint all in new font repaintText = true; repaintHScroll = true; repaintVScroll = true; paint(getGraphics()); super.setFont(f); } /* ** Paint */ public void paint(Graphics g) { if(g == null) return; //textPainter.setCursor(((TextArea)component).isEditable()); // set scrollpane window to new size if necessary if(component.getSize().width<=0 || component.getSize().height<=0) { //System.out.println("TextArea: skipping paint() for lack of size"); return; // nothing to paint when no place to paint it } if(backgroundImage == null || !totalScreen.equals(component.getSize())) { // new field and scrollbar calculations setFields(component.getSize()); //new background image backgroundImage = createImage(totalScreen.width, totalScreen.height); if( backgroundImage!= null && backgroundGraphics!= null) { backgroundGraphics.dispose(); } backgroundGraphics = backgroundImage.getGraphics(); //redraw all: scrollbars and text repaintText = true; repaintHScroll = true; repaintVScroll = true; } //set background colors if necessary Color back = component.getBackground(); Color fore = component.getForeground(); boolean setcolors = false; if(fore!=null && !fore.equals(textAreaColors[4])) { textAreaColors[4]=fore; setcolors = true; } if(back != null && !back.equals(textAreaColors[0])) { textAreaColors = RudolphPeer.getBarColors(back, textAreaColors[4]); setcolors = true; } if(setcolors){ if(hScroll != null) { hScroll.setBarColors(textAreaColors); } vScroll.setBarColors(textAreaColors); textPainter.setTextColors(textAreaColors); } //paint viewport(if there is something to paint, this is) if(repaintText && textScreen.width>2 && textScreen.height > 2) { //text painter //backgroundGraphics.clearRect(1,1,textScreen.width-2, textScreen.height-2); backgroundGraphics.setColor(textAreaColors[0]); backgroundGraphics.fillRect(1, 1, textScreen.width - 2, textScreen.height - 2); wonka.vm.Etc.woempa(9, "before textPainter.paint"); if(selectionStart == selectionStop) { // no selection, display cursor textPainter.paint(textScreen.width, textScreen.height, cursorLine, cursorBufferOffset, cursorHorizontalOffset, backgroundGraphics); } else { //display textarea with selected text textPainter.paint(textScreen.width, textScreen.height, startLine, startBufferOffset, startHorizontalOffset, stopLine, stopBufferOffset, stopHorizontalOffset, backgroundGraphics); } repaintText = false; } //paint scrollbars if needed if(hScroll != null && repaintHScroll ) { hScroll.paint(backgroundGraphics); repaintHScroll = false; } if(vScrollVisible && repaintVScroll) { vScroll.paint(backgroundGraphics); repaintVScroll = false; } // paint background image g.drawImage(backgroundImage, 0, 0, component); super.paint(g); } /* ** Auxilliary: set cursor, selection start&stop according to new value ** the data struct newposition is an int[4] return from a setScreenPosition function with following elements: ** => newposition[0] : position of the selected character in the original text ** => newposition[1] : line of the selected character on the screen ** => newposition[2] : horizontal offset in pixels (or offset calculation value) of the selected character from the left side of the screen ** => newposition[3] : position of the selected character in the TextPainter's text buffer (the text without the \n line breaks) */ private boolean setNewSelection(int[] newposition) { // Rectangle p = tempData) { if(!initialized) init(); // see in which case we are if(newposition[0]== position) { // nothing changed return false; } //else {... if(selectionStart == selectionStop && newposition[0]<position) { // new selection starting at new position, ending at cursor selectionStart = newposition[0]; startLine = newposition[1]; startHorizontalOffset = newposition[2]; startBufferOffset = newposition[3]; selectionStop = position; stopLine=cursorLine; stopHorizontalOffset=cursorHorizontalOffset; stopBufferOffset=cursorBufferOffset; } else if(selectionStart == selectionStop && newposition[0]>position) { // new selection starting at cursor, ending at new position selectionStart = position; startLine=cursorLine; startHorizontalOffset=cursorHorizontalOffset; startBufferOffset=cursorBufferOffset; selectionStop = newposition[0]; stopLine = newposition[1]; stopHorizontalOffset = newposition[2]; stopBufferOffset = newposition[3]; } else if(position == selectionStart && newposition[0]>selectionStop){ // inverse the selection: the old stop becomes the new start, new position becomes the new stop selectionStart = selectionStop; startLine=stopLine; startHorizontalOffset=stopHorizontalOffset; startBufferOffset=stopBufferOffset; selectionStop = newposition[0]; stopLine = newposition[1]; stopHorizontalOffset = newposition[2]; stopBufferOffset = newposition[3]; } else if(position == selectionStart && newposition[0]==selectionStop){ //invalidate selection by setting them all to new position selectionStart=newposition[0]; //selectionStop=newposition[0]; } else if(position == selectionStart){ // nothing special, just use new data for new start selectionStart = newposition[0]; startLine = newposition[1]; startHorizontalOffset = newposition[2]; startBufferOffset = newposition[3]; } //else if(position == selectionStop && newposition[0]<selectionStart) { else if(newposition[0]<selectionStart) { // big swap, old start becomes new stop, newposition becones new start selectionStop = selectionStart; stopLine=startLine; stopHorizontalOffset=startHorizontalOffset; stopBufferOffset=startBufferOffset; selectionStart = newposition[0]; startLine = newposition[1]; startHorizontalOffset = newposition[2]; startBufferOffset = newposition[3]; } //else if(position == selectionStop && newposition[0] == selectionStart) { else if(newposition[0] == selectionStart) { //invalidate selection by setting them all to new position //selectionStart=newposition[0]; selectionStop=newposition[0]; } //else if(position == selectionStop) { else { // just set stop to new value selectionStop = newposition[0]; stopLine = newposition[1]; stopHorizontalOffset = newposition[2]; stopBufferOffset = newposition[3]; } // in all cases, new position becomes cursor position = newposition[0]; cursorLine=newposition[1]; cursorHorizontalOffset=newposition[2]; cursorBufferOffset=newposition[3]; return true; //} } /* ** Auxilliary: recalculate screen and repaint for horizontal or vertical scrollbar ** (hiis function is class-protected so it can be called from withing the scroll runner thread just as well */ public void repaint(ScrollPainter alignation) { if(alignation == vScroll) { // vertical scrolling textPainter.setLineOffset( vScroll.getBarPos() ); repaintVScroll= true; } else { // horizontal scrolling textPainter.setTextOffset( hScroll.getBarPos() ); repaintHScroll= true; } // also repaint the (scrolled) text area repaintText = true; paint(getGraphics()); } /* ** Auxilliary check if position (line,offset) in current viewport and adjust viewport if not ** (return true if adjustment performed => repaint needed */ private boolean adjustHorizontalViewport(int offset) { // we assume a check on hScroll beforehand boolean viewportadjusted = false; if(offset<textPainter.getTextOffset()) { textPainter.setTextOffset(cursorHorizontalOffset); hScroll.setBarPos(cursorHorizontalOffset); viewportadjusted = true; } else { int topline= offset - textPainter.getViewportWidth()+1; if(topline>textPainter.getTextOffset()) { textPainter.setTextOffset(topline); hScroll.setBarPos(topline); viewportadjusted = true; } } return viewportadjusted; } private boolean adjustVerticalViewport(int line) { boolean viewportadjusted = false; if(line < textPainter.getLineOffset() ) { // above current screen => move screen up textPainter.setLineOffset(cursorLine); vScroll.setBarPos(cursorLine); viewportadjusted = true; } else { int topline = line - textPainter.getViewportLines()+1; if(topline > textPainter.getLineOffset()) { // below current screen => move screen down textPainter.setLineOffset(topline); vScroll.setBarPos(topline); viewportadjusted = true; } } return viewportadjusted; } /* ** Auxilliary functions: set new tempData[] type data for position, selection start, selection stop, return true if valid */ private boolean updatePosition(int newpos){ if(newpos<0 || newpos>text.length()) { return false; } //else textPainter.setScreenPosition(newpos, tempData); position=tempData[0]; cursorLine=tempData[1]; cursorHorizontalOffset=tempData[2]; cursorBufferOffset=tempData[3]; super.setCaretPosition(newpos); return true; } private boolean updateStart(int newpos){ if(newpos<0 || newpos>text.length()) { return false; } //else textPainter.setScreenPosition(newpos, tempData); selectionStart=tempData[0]; startLine=tempData[1]; startBufferOffset=tempData[2]; startHorizontalOffset=tempData[3]; return true; } private boolean updateStop(int newpos){ if(newpos<0 || newpos>text.length()) { return false; } //else textPainter.setScreenPosition(newpos, tempData); selectionStop=tempData[0]; stopLine=tempData[1]; stopBufferOffset=tempData[2]; stopHorizontalOffset=tempData[3]; return true; } /* ** set cursor to data and collapse selection to it */ private boolean updateCursor(int[] newdata) { if(position == newdata[0]) { return false; } //else position = newdata[0]; cursorLine = newdata[1]; cursorHorizontalOffset = newdata[2]; cursorBufferOffset = newdata[3]; selectionStart = position; selectionStop = position; return true; } /* ** For a given size: calculate scrollbar position and text area width ** and set the scrollposition if necessary */ private void setFields(Dimension newsize) { //System.out.println("... TextArea calculating settings for new size"+newsize); totalScreen.setSize(newsize); textScreen.setSize(newsize); //if horizontal scrollbar, set new offset and subtract scrollbar height from textarea size if(hScroll != null) { textScreen.height -= hScroll.getMinimumThickness(); hScroll.setOffset(textScreen.height); } //if horizontal scrollbar: set new offset and subtract scrollbar width from textarea size if(vScrollVisible) { textScreen.width -= vScroll.getMinimumThickness(); vScroll.setOffset(textScreen.width); } //new scrollbar width and viewport textPainter.setSize(textScreen); if(hScroll != null) { hScroll.setScreenWidth(textScreen.width, textPainter.getViewportWidth(), textPainter.getMaximumWidth()); } vScroll.setScreenHeight( textScreen.height, textPainter.getViewportLines(),textPainter.getMaximumLines()); //also recalculate PointData for position and selection(line wrapping might hace changed their lines) updatePosition(position); updateStart(selectionStart); updateStop(selectionStop); } /* ** Keyboard support : ** overrides the TextComponent keyboard to deal with multi-line display and up-down keys ** => use cursor keys left-right-home-end AND up-down-pgUp,pgDn ** => use cursor keys with shift for selection ** => use delete and backspace for cursor or selection ** => insert char for cursor and selection */ /* ** Mouse clicked (nothing special) */ public void mouseClicked(MouseEvent e) { } /* ** Mouse entered (nothing special) */ public void mouseEntered(MouseEvent e) { } public void mouseMoved(MouseEvent e) { } /* ** Mouse Pressed: ** =>if in text: set insert position ** =>if in scrollbar scrollbox: set mouse position for scrollbox dragging ** =>if in scrollbar up/down boxes or above/below scrollbox: call thread for viewport movements */ public void mousePressed(MouseEvent e) { int x=e.getX(); int y=e.getY(); if(x<textScreen.width && y<textScreen.height) { //inside text area lastMousePosition.setLocation(x,y);//detecting movement when scrolling //get point in x,y coordinates,position,width... textPainter.setScreenPosition(x,y,tempData); // set data to cursor and collapse selection to that point, repaint if needed if(updateCursor(tempData) ){ repaintText = true; paint(getGraphics()); } } else if(hScroll!= null && y>textScreen.height){ //inside horizontal scrollbar int active = hScroll.setActive(x,y); if(active == AdjustmentEvent.TRACK) {//scrollbox clicked lastMousePosition.setLocation(x,y); repaintHScroll = true; paint(getGraphics()); //show active scroll box of bar } else if(active == AdjustmentEvent.UNIT_DECREMENT && hScroll.lineUp() ) { // launch scroll thread mouseEventThread.setRunner(hScroll,component); // managed to set scrollbar one char left repaint(hScroll); } else if(active == AdjustmentEvent.UNIT_DECREMENT) { // simply repaint the scrollbar with the 'up' block pressed repaintHScroll = true; paint(getGraphics()); } else if(active==AdjustmentEvent.UNIT_INCREMENT && hScroll.lineDn() ) { // launch scroll thread mouseEventThread.setRunner(hScroll,component); // managed to set scrollbar one char right repaint(hScroll); } else if(active==AdjustmentEvent.UNIT_INCREMENT) { // simply repaint the scrollbar with the 'down' block pressed repaintHScroll = true; paint(getGraphics()); } else if(active==AdjustmentEvent.BLOCK_DECREMENT && hScroll.pageUp() ) { // launch scroll thread mouseEventThread.setRunner(hScroll,component); // set text and repaint repaint(hScroll); // the area between button and scrollbox can not be 'pressed',so don'tbother to redraw } else if(active==AdjustmentEvent.BLOCK_INCREMENT && hScroll.pageDn() ) { // launch scroll thread mouseEventThread.setRunner(hScroll,component); // set text and repaint repaint(hScroll); // the area between button and scrollbox can not be 'pressed',so don'tbother to redraw } } else if(vScrollVisible && x>textScreen.width) { //inside vertical scrollbar int active = vScroll.setActive(x,y); if(active == AdjustmentEvent.TRACK) { //scrollbox clicked lastMousePosition.setLocation(x,y); repaintVScroll = true; paint(getGraphics()); //show active scroll box of bar } else if(active == AdjustmentEvent.UNIT_DECREMENT && vScroll.lineUp() ) { // launch scroll thread mouseEventThread.setRunner(vScroll,component); // set text and repaint repaint(vScroll); } else if(active == AdjustmentEvent.UNIT_DECREMENT) { // simply repaint the scrollbar with the 'up' block pressed repaintVScroll = true; paint(getGraphics()); } else if(active==AdjustmentEvent.UNIT_INCREMENT && vScroll.lineDn() ) { // launch scroll thread mouseEventThread.setRunner(vScroll,component); // set text and repaint repaint(vScroll); } else if(active==AdjustmentEvent.UNIT_INCREMENT) { // simply repaint the scrollbar with the 'down' block pressed repaintVScroll = true; paint(getGraphics()); } else if(active==AdjustmentEvent.BLOCK_DECREMENT && vScroll.pageUp() ) { // launch scroll thread mouseEventThread.setRunner(vScroll,component); // set text and repaint repaint(vScroll); // the area between button and scrollbox can not be 'pressed',so don'tbother to redraw } else if(active==AdjustmentEvent.BLOCK_INCREMENT && vScroll.pageDn() ) { // launch scroll thread mouseEventThread.setRunner(vScroll,component); // set text and repaint repaint(vScroll); // the area between button and scrollbox can not be 'pressed',so don'tbother to redraw } } } /* ** Mouse released: ** =>set scrolling to stop and scrollbars to no-selected */ public void mouseReleased(MouseEvent e) { //scrollbars are no longer selected if(hScroll!= null) { if(hScroll.isSelected()) { hScroll.setNoSelected(); repaintHScroll=true; } mouseEventThread.stopRunner(hScroll); } if(vScrollVisible && vScroll.isSelected()) { vScroll.setNoSelected(); repaintVScroll = true; } mouseEventThread.stopRunner(vScroll); if(repaintHScroll || repaintVScroll) { paint(getGraphics()); } } /* ** Mouse exited as per mouseReleased, scrolling stops */ public void mouseExited(MouseEvent e) { //scrollbars are no longer selected if(hScroll!= null) { if(hScroll.isSelected()) { hScroll.setNoSelected(); repaintHScroll=true; } mouseEventThread.stopRunner(hScroll); } if(vScrollVisible && vScroll.isSelected()) { vScroll.setNoSelected(); repaintVScroll = true; } mouseEventThread.stopRunner(vScroll); if(repaintHScroll || repaintVScroll) { paint(getGraphics()); } } /* ** Mouse dragged: ** => look for text scrolling mode and if needed, start thread for viewport scrolling ** => if in viewport, 'drag' a selection from insert point to current mouse position ** => if in scrollbar box: move box and update viewport */ public void mouseDragged(MouseEvent e) { int x=e.getX(); int y=e.getY(); if(hScroll!= null && hScroll.getActive() == AdjustmentEvent.TRACK) { if(hScroll.moveBar(x-lastMousePosition.x)) { //set the new text area line offset textPainter.setTextOffset( hScroll.getBarPos() ); lastMousePosition.setLocation(x,y); repaintText = true; repaintHScroll = true; paint(getGraphics()); //show new scrollbar and screen selection } } else if(vScrollVisible && vScroll.getActive() == AdjustmentEvent.TRACK) { if(vScroll.moveBar(y-lastMousePosition.y)) { //set the new text area line offset textPainter.setLineOffset( vScroll.getBarPos() ); lastMousePosition.setLocation(x,y); repaintText = true; repaintVScroll = true; paint(getGraphics()); //show new scrollbar and screen selection } } else if(x<textScreen.width && y<textScreen.height) { //inside text area //Horizontal mouse scrolling if(hScroll != null) { if(y<0 || y>textScreen.height) { // no longer in list area, so stop mouseEventThread.stopRunner(hScroll); } else if(x>0 && x<textPainter.getBorder() && x<lastMousePosition.x && hScroll.lineUp()) { // in upper area moved up => set scrollbar one up & start thread (moving up) mouseEventThread.setRunner(hScroll,component,ScrollRunner.SCROLL_UP); repaint(vScroll); lastMousePosition.x=x; } else if(x>textPainter.getBorder() && x<lastMousePosition.x){ //moved up when not in upper area => stop (moving down) thread mouseEventThread.stopRunner(hScroll); lastMousePosition.x=x; } else if(x<textScreen.width && x>(textScreen.width-textPainter.getBorder()) && x>lastMousePosition.x && hScroll.lineDn()){ // in lower area moved down: set scrollbar one down and start thread (moving down) mouseEventThread.setRunner(hScroll,component,ScrollRunner.SCROLL_DOWN); repaint(vScroll); lastMousePosition.x=x; } else if(x<(textScreen.width-textPainter.getBorder()) && x>lastMousePosition.x){ //moved down when not in lower area => stop (moving up) thread mouseEventThread.stopRunner(hScroll); lastMousePosition.x=x; } } //ALWAYS vertical mouse scrolling (even if we don't see the vertical scrollbar, it's there invisible specially to do the calculations) if(x<0 || x>textScreen.width) { // no longer in list area, so stop mouseEventThread.stopRunner(vScroll); } else if(y>0 && y<textPainter.getBorder() && y<lastMousePosition.y && vScroll.lineUp()) { // in upper area moved up => set scrollbar one up & start thread (moving up) mouseEventThread.setRunner(vScroll,component,ScrollRunner.SCROLL_UP); repaint(vScroll); lastMousePosition.y=y; } else if(y>textPainter.getBorder() && y<lastMousePosition.y){ //moved up when not in upper area => stop (moving down) thread mouseEventThread.stopRunner(vScroll); lastMousePosition.y=y; } else if(y<textScreen.height && y>(textScreen.height-textPainter.getBorder()) && y>lastMousePosition.y && vScroll.lineDn()){ // in lower area moved down: set scrollbar one down and start thread (moving down) mouseEventThread.setRunner(vScroll,component,ScrollRunner.SCROLL_DOWN); repaint(vScroll); lastMousePosition.y=y; } else if(y<(textScreen.height-textPainter.getBorder()) && y>lastMousePosition.y){ //moved down when not in lower area => stop (moving up) thread mouseEventThread.stopRunner(vScroll); lastMousePosition.y=y; } //(end vertical scrollbar // finaly get a selection out of the text the cursor was draged over textPainter.setScreenPosition(x,y,tempData); if (setNewSelection(tempData) ) { // the new position changed the selection (or cursor position) //new selection position lastMousePosition.setLocation(x,y); // repaint repaintText = true; paint(getGraphics()); } } } /* ** text methods inherited from superclass TextComponent */ /* ** TextComponent.setText : Overwritten to also update the visible parts */ public synchronized void setText(String newtext) { if(!initialized) init(); super.setText(newtext); //set new text text = newtext; // let painter calculate new image and tell scrollbars textPainter.setImage(text); // tell scrollbars and set offset to null if(hScroll != null) { hScroll.setBarRange(textPainter.getMaximumWidth()); hScroll.setBarPos(0); //(also set scrollbars to end of text } //vScroll not always visible, but always present (Calculating positions for mouse selection scrolling) vScroll.setBarRange(textPainter.getMaximumLines()); vScroll.setBarPos(0); textPainter.setOffset(0,0); // set cursor to end of text, collapse selection to cursor // updatePosition(newtext.length()); updatePosition(0); /* if(((TextArea)component).isEditable()) { setCaretPosition(newtext.length()); } else { setCaretPosition(0); } */ selectionStart = position; selectionStop = position; // repaint repaintText = true; repaintHScroll = true; repaintVScroll = true; paint(getGraphics()); //tell the text listeners fireTextEvent(); } /* ** TextComponent.setCaretPosition : Overwritten to also calculate the caret's PointData and show the caret on screen */ public void setCaretPosition(int newpos) { if(!initialized) init(); if(newpos!=position && updatePosition(newpos)) { // adjust viewport if needed repaintVScroll = adjustVerticalViewport(cursorLine); if(hScroll != null) { repaintHScroll = adjustHorizontalViewport(cursorHorizontalOffset); } repaintText = true; paint(getGraphics()); } } /* ** TextComponent.setSelectionStart : Overwritten to also calculate the caret's PointData and show the caret on screen */ /* ** TODO: These methods do not exist in the peer !!!!!!!!!!! */ public synchronized void setSelectionStart(int newpos) { if(!initialized) init(); if(newpos!=position && updateStart(newpos)) { // adjust viewport if needed repaintVScroll = adjustVerticalViewport(startLine); if(hScroll != null) { repaintHScroll = adjustHorizontalViewport(startHorizontalOffset); } repaintText = true; paint(getGraphics()); } } /* ** TextComponent.setSelectionStop : Overwritten to also calculate the caret's PointData and show the caret on screen */ public synchronized void setSelectionEnd(int newpos) { if(!initialized) init(); if(newpos!=position && updateStop(newpos)) { // adjust viewport if needed repaintVScroll = adjustVerticalViewport(stopLine); if(hScroll != null) { repaintHScroll = adjustHorizontalViewport(stopHorizontalOffset); } repaintText = true; paint(getGraphics()); } } /* ** Own protected helper function: fires a textValueChanged(textEvent) on the registered textListener(s) */ protected void fireTextEvent() { component.dispatchEvent(new TextEvent(component, TextEvent.TEXT_VALUE_CHANGED)); } /* ** internal scrollpainter for horizontal scrollbar */ class HAreaScrollPainter extends ScrollPainter { /* ** Constructor : set height, lineup/lineDn and minimum box width for horizontal scrollbar */ public HAreaScrollPainter() { super(RudolphScrollbarPeer.HSCROLL_HEIGHT, RudolphScrollbarPeer.HSCROLL_LINEUPWIDTH, RudolphScrollbarPeer.HSCROLL_LINEDNWIDTH, RudolphScrollbarPeer.HSCROLL_MINIMUMBOXWIDTH); } /* ** scrollbar width and scrollbar position out of (x,y) dimension ** the abstract ScrollPainter classes filled in for horizontal scrollbar */ /* ** (horizontal bar) x- position in scrollbar from point (x,y) */ public int getPos(int x, int y) { return x; } /* ** (horizontal bar) scrollbar height as thickness from dimension (width, height) */ public int getThickness(int width, int height) { return height; } /* ** scrollbar length and thickness to (width, height) dimension for horizontal and vertical scrollbar */ public Dimension getSize(int scrollbarlength, int scrollbarthickness) { return new Dimension(scrollbarlength, scrollbarthickness); } /* ** replace <thickness> of given dimension by scrollban minimum thickness */ public Dimension getPreferredSize(int width, int height){ return new Dimension(width, minimumThickness); } /* ** GetSize derived functions:specific overwrite for horizontal scrollbar */ public Dimension getMinimumSize() { return new Dimension(lineUpSpan+lineDnSpan+minimumScreenSpan , minimumThickness); } public Dimension getPreferredSize() { return new Dimension(lineUpSpan+lineDnSpan+screenRange , minimumThickness); } public Dimension getCurrentSize() { return new Dimension(lineUpSpan+lineDnSpan+screenRange , minimumThickness); } public void setThickness(int width, int height) { currentThickness =(height>minimumThickness)?height:minimumThickness; } public boolean setRange(int width, int height) { return setRange(width); } /* ** set a new screen height and visibility after textarea rescaling */ public boolean setScreenWidth(int totalwidth, int newvisible, int newrange) { screenRange = totalwidth - lineUpSpan - lineDnSpan; barSpan = newvisible; barRange = newrange; blockStep = (newvisible>lineStep)?newvisible-lineStep:lineStep; if (screenRange <=0) { // crippled mode screenRange = 0; screenSpan = 0; screenPos = 0; crippledSpan = totalwidth; } else { //set new screen range and visibility bar span crippledSpan = -1; //safety check if(barSpan > barRange) { barSpan = barRange; barPos = 0; } else if((barPos+barSpan)>barRange) { barPos = barRange - barSpan; } //calculate screen scrollbox span and -position setScreen(); //setBar(minimumspan); } //changing the width always forces a redraw, so return true for every case return true; } /* ** basic functions : get field in which probing point exists ** additional feature: next to the x-position(scrollbar length) ** we also look if the y-position is within the range (offset , offset + height) */ synchronized public int setActive(int x, int y) { currentActive = (y>barOffset)?getField(x): RudolphScrollbarPeer.FIELD_NONESELECTED; return currentActive; } /* ** paint command */ public void paint(Graphics g) { if(crippledSpan<0) { RudolphScrollbarPeer.paintHScrollbar(0,barOffset,minimumThickness, paintedScreenPos,screenSpan,screenRange, currentActive,barColors,g); } else { RudolphScrollbarPeer.paintCrippledHScrollbar(0,barOffset,minimumThickness, crippledSpan, currentActive,barColors,g); } } //end of inner class HAreaScrollPainter } /* ** internal scrollpainter for vertical scrollbar */ class VAreaScrollPainter extends ScrollPainter { /* ** Constructor : set height, lineup/lineDn and minimum box width for horizontal scrollbar */ public VAreaScrollPainter() { super(RudolphScrollbarPeer.VSCROLL_WIDTH, RudolphScrollbarPeer.VSCROLL_LINEUPHEIGHT, RudolphScrollbarPeer.VSCROLL_LINEDNHEIGHT, RudolphScrollbarPeer.VSCROLL_MINIMUMBOXHEIGHT); } /* ** scrollbar width and scrollbar position out of (x,y) dimension ** the abstract ScrollPainter classes filled in for horizontal scrollbar */ /* ** (horizontal bar) x- position in scrollbar from point (x,y) */ public int getPos(int x, int y) { return y; } /* ** (horizontal bar) scrollbar height as thickness from dimension (width, height) */ public int getThickness(int width, int height) { return width; } /* ** scrollbar length and thickness to (width, height) dimension for horizontal and vertical scrollbar */ public Dimension getSize(int scrollbarlength, int scrollbarthickness) { return new Dimension(scrollbarthickness, scrollbarlength); } /* ** replace <thickness> of given dimension by scrollban minimum thickness */ public Dimension getPreferredSize(int width, int height) { return new Dimension(minimumThickness,height); } /* ** GetSize derived functions:specific overwrite for horizontal scrollbar */ public Dimension getMinimumSize() { return new Dimension(minimumThickness, lineUpSpan+lineDnSpan+minimumScreenSpan); } public Dimension getPreferredSize() { return new Dimension(minimumThickness, lineUpSpan+lineDnSpan+screenRange); } public Dimension getCurrentSize() { return new Dimension(minimumThickness, lineUpSpan+lineDnSpan+screenRange); } public void setThickness(int width, int height) { currentThickness =(width>minimumThickness)?width:minimumThickness; } public boolean setRange(int width, int height) { return setRange(height); } /* ** set a new screen height and visibility after textarea rescaling */ public boolean setScreenHeight(int totalheight, int newvisible, int newrange) { screenRange = totalheight - RudolphScrollbarPeer.VSCROLL_LINEUPHEIGHT - RudolphScrollbarPeer.VSCROLL_LINEUPHEIGHT; barSpan = newvisible; barRange = newrange; blockStep=(newvisible>1)?newvisible-1:1; if (screenRange <=0) { //crippled scrollbar screenRange = 0; screenSpan = 0; screenRange = 0; crippledSpan = totalheight; } else { //set new screen range and visibility bar span crippledSpan = -1; //safety check if(barSpan > barRange) { barSpan = barRange; barPos = 0; } else if((barPos+barSpan)>barRange) { barPos = barRange - barSpan; } //calculate screen scrollbox span and -position setScreen(); //setBar(minimumspan); } //always return true for redrawing return true; } /* ** basic functions : get field in which probing point exists: Special case: we take into account the offset */ synchronized public int setActive(int x, int y) { currentActive = (x>barOffset)?getField(y):RudolphScrollbarPeer.FIELD_NONESELECTED; return currentActive; } /* ** paint command */ public void paint(Graphics g) { if(crippledSpan<0) { RudolphScrollbarPeer.paintVScrollbar(barOffset,0,minimumThickness, paintedScreenPos,screenSpan,screenRange, currentActive,barColors,g); } else { RudolphScrollbarPeer.paintCrippledVScrollbar(barOffset,0,minimumThickness, crippledSpan, currentActive, barColors,g); } } //end of inner class HAreaScrollPainter } public boolean inRange(MouseEvent e) { return (e.getX() < textScreen.width && e.getY() < textScreen.height); } public void focusGained(FocusEvent event) { textPainter.setCursor(((TextArea)component).isEditable()); this.textPainter.setScreenPositionLine(0, 2, tempData); tempData[0] = 0; tempData[3] = 0; updateCursor(tempData); repaintText = true; paint(getGraphics()); } public void focusLost(FocusEvent event) { this.textPainter.setCursor(false); repaintText = true; paint(getGraphics()); } }