Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 3148 Details for
Bug 19825
[misc] Right clicking in an editor window should operate where clicked
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Right click fix
StyledText.patch (text/plain), 477.37 KB, created by
Ron Baldwin
on 2003-01-26 23:18:20 EST
(
hide
)
Description:
Right click fix
Filename:
MIME Type:
Creator:
Ron Baldwin
Created:
2003-01-26 23:18:20 EST
Size:
477.37 KB
patch
obsolete
>Index: StyledText.java >=================================================================== >RCS file: /home/eclipse/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java,v >retrieving revision 1.138 >diff -u -r1.138 StyledText.java >--- StyledText.java 24 Jan 2003 17:20:41 -0000 1.138 >+++ StyledText.java 27 Jan 2003 02:30:40 -0000 >@@ -1,9 +1,9 @@ > package org.eclipse.swt.custom; > > /* >- * Copyright (c) 2000, 2002 IBM Corp. All rights reserved. >- * This file is made available under the terms of the Common Public License v1.0 >- * which accompanies this distribution, and is available at >+ * Copyright (c) 2000, 2002 IBM Corp. All rights reserved. >+ * This file is made available under the terms of the Common Public License v1.0 >+ * which accompanies this distribution, and is available at > * http://www.eclipse.org/legal/cpl-v10.html > */ > >@@ -18,22 +18,22 @@ > import org.eclipse.swt.widgets.*; > > /** >- * A StyledText is an editable user interface object that displays lines >- * of text. The following style attributes can be defined for the text: >+ * A StyledText is an editable user interface object that displays lines >+ * of text. The following style attributes can be defined for the text: > * <ul> >- * <li>foreground color >+ * <li>foreground color > * <li>background color > * <li>font style (bold, regular) > * </ul> > * <p> >- * In addition to text style attributes, the background color of a line may >+ * In addition to text style attributes, the background color of a line may > * be specified. > * </p> > * <p> >- * There are two ways to use this widget when specifying text style information. >- * You may use the API that is defined for StyledText or you may define your own >- * LineStyleListener. If you define your own listener, you will be responsible >- * for maintaining the text style information for the widget. IMPORTANT: You may >+ * There are two ways to use this widget when specifying text style information. >+ * You may use the API that is defined for StyledText or you may define your own >+ * LineStyleListener. If you define your own listener, you will be responsible >+ * for maintaining the text style information for the widget. IMPORTANT: You may > * not define your own listener and use the StyledText API. The following > * StyledText API is not supported if you have defined a LineStyleListener: > * <ul> >@@ -46,11 +46,11 @@ > * </p> > * <p> > * There are two ways to use this widget when specifying line background colors. >- * You may use the API that is defined for StyledText or you may define your own >- * LineBackgroundListener. If you define your own listener, you will be responsible >- * for maintaining the line background color information for the widget. >- * IMPORTANT: You may not define your own listener and use the StyledText API. >- * The following StyledText API is not supported if you have defined a >+ * You may use the API that is defined for StyledText or you may define your own >+ * LineBackgroundListener. If you define your own listener, you will be responsible >+ * for maintaining the line background color information for the widget. >+ * IMPORTANT: You may not define your own listener and use the StyledText API. >+ * The following StyledText API is not supported if you have defined a > * LineBackgroundListener: > * <ul> > * <li>getLineBackground(int) >@@ -60,7 +60,7 @@ > * <p> > * The content implementation for this widget may also be user-defined. To do so, > * you must implement the StyledTextContent interface and use the StyledText API >- * setContent(StyledTextContent) to initialize the widget. >+ * setContent(StyledTextContent) to initialize the widget. > * </p> > * <p> > * IMPORTANT: This class is <em>not</em> intended to be subclassed. >@@ -71,1480 +71,1486 @@ > * </dl> > */ > public class StyledText extends Canvas { >- static final char TAB = '\t'; >- static final String PlatformLineDelimiter = System.getProperty("line.separator"); >- static final int BIDI_CARET_WIDTH = 4; >- static final int XINSET = BIDI_CARET_WIDTH - 1; >- static final int DEFAULT_WIDTH = 64; >- static final int DEFAULT_HEIGHT = 64; >- >- static final int ExtendedModify = 3000; >- static final int LineGetBackground = 3001; >- static final int LineGetStyle = 3002; >- static final int TextChanging = 3003; >- static final int TextSet = 3004; >- static final int VerifyKey = 3005; >- static final int TextChanged = 3006; >- static final int LineGetSegments = 3007; >- >- Color selectionBackground; // selection background color >- Color selectionForeground; // selection foreground color >- StyledTextContent logicalContent; // native content (default or user specified) >- StyledTextContent content; // line wrapping content, same as logicalContent if word wrap is off >- DisplayRenderer renderer; >- TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent >- DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered >- LineCache lineCache; >- boolean userLineStyle = false; // true=widget is using a user defined line style listener for line styles. false=widget is using the default line styler to store line styles >- boolean userLineBackground = false; // true=widget is using a user defined line background listener for line backgrounds. false=widget is using the default line styler to store line backgrounds >- int verticalScrollOffset = 0; // pixel based >- int horizontalScrollOffset = 0; // pixel based >- int topIndex = 0; // top visible line >- int topOffset = 0; // offset of first character in top line >- int clientAreaHeight = 0; // the client area height. Needed to calculate content width for new >- // visible lines during Resize callback >- int clientAreaWidth = 0; // the client area width. Needed during Resize callback to determine >- // if line wrap needs to be recalculated >- int lineHeight; // line height=font height >- int tabLength = 4; // number of characters in a tab >- int lineEndSpaceWidth; // space, in pixel, used to indicated a selected line break >- int leftMargin = 1; >- int topMargin = 1; >- int rightMargin = 2; >- int bottomMargin = 2; >- Cursor ibeamCursor; >- int columnX; // keep track of the horizontal caret position >- // when changing lines/pages. Fixes bug 5935 >- int caretOffset = 0; >- Point selection = new Point(0, 0); // x is character offset, y is length >- int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text >- Point doubleClickSelection; // selection after last mouse double click >- boolean editable = true; >- boolean wordWrap = false; >- boolean doubleClickEnabled = true; // see getDoubleClickEnabled >- boolean overwrite = false; // insert/overwrite edit mode >- int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default. >- Hashtable keyActionMap = new Hashtable(); >- Color background = null; // workaround for bug 4791 >- Color foreground = null; // >- Clipboard clipboard; >- boolean mouseDoubleClick = false; // true=a double click ocurred. Don't do mouse swipe selection. >- int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left) >- int lastTextChangeStart; // cache data of the >- int lastTextChangeNewLineCount; // last text changing >- int lastTextChangeNewCharCount; // event for use in the >- int lastTextChangeReplaceLineCount; // text changed handler >- int lastTextChangeReplaceCharCount; >- boolean isBidi; >- boolean bidiColoring = false; // apply the BIDI algorithm on text segments of the same color >- Image leftCaretBitmap = null; >- Image rightCaretBitmap = null; >- int caretDirection = SWT.NULL; >- PaletteData caretPalette = null; >- int lastCaretDirection = SWT.NULL; >- boolean isCarbon; // flag set to true on Mac OSX >- >- /** >- * The Printing class implements printing of a range of text. >- * An instance of <class>Printing </class> is returned in the >- * StyledText#print(Printer) API. The run() method may be >- * invoked from any thread. >- */ >- class Printing implements Runnable { >- final static int LEFT = 0; // left aligned header/footer segment >- final static int CENTER = 1; // centered header/footer segment >- final static int RIGHT = 2; // right aligned header/footer segment >- >- Printer printer; >- PrintRenderer renderer; >- StyledTextPrintOptions printOptions; >- StyledTextContent printerContent; // copy of the widget content >- Rectangle clientArea; // client area to print on >- Font printerFont; >- FontData displayFontData; >- Hashtable printerColors; // printer color cache for line backgrounds and style >- Hashtable lineBackgrounds = new Hashtable(); // cached line backgrounds >- Hashtable lineStyles = new Hashtable(); // cached line styles >- Hashtable bidiSegments = new Hashtable(); // cached bidi segments when running on a bidi platform >- GC gc; // printer GC >- int pageWidth; // width of a printer page in pixels >- int startPage; // first page to print >- int endPage; // last page to print >- int pageSize; // number of lines on a page >- int startLine; // first (wrapped) line to print >- int endLine; // last (wrapped) line to print >- boolean singleLine; // widget single line mode >- Point selection = null; // selected text >- >- /** >- * Creates an instance of <class>Printing</class>. >- * Copies the widget content and rendering data that needs >- * to be requested from listeners. >- * </p> >- * @param parent StyledText widget to print. >- * @param printer printer device to print on. >- * @param printOptions print options >- */ >- Printing(StyledText parent, Printer printer, StyledTextPrintOptions printOptions) { >- PrinterData data = printer.getPrinterData(); >- >- this.printer = printer; >- this.printOptions = printOptions; >- singleLine = parent.isSingleLine(); >- startPage = 1; >- endPage = Integer.MAX_VALUE; >- if (data.scope == PrinterData.PAGE_RANGE) { >- startPage = data.startPage; >- endPage = data.endPage; >- if (endPage < startPage) { >- int temp = endPage; >- endPage = startPage; >- startPage = temp; >- } >- } if (data.scope == PrinterData.SELECTION) { >- selection = parent.getSelectionRange(); >- } >- >- displayFontData = getFont().getFontData()[0]; >- copyContent(parent.getContent()); >- cacheLineData(printerContent); >- } >- /** >- * Caches the bidi segments of the given line. >- * </p> >- * @param lineOffset offset of the line to cache bidi segments for. >- * Relative to the start of the document. >- * @param line line to cache bidi segments for. >- */ >- void cacheBidiSegments(int lineOffset, String line) { >- int[] segments = getBidiSegments(lineOffset, line); >- >- if (segments != null) { >- bidiSegments.put(new Integer(lineOffset), segments); >- } >- } >- /** >- * Caches the line background color of the given line. >- * </p> >- * @param lineOffset offset of the line to cache the background >- * color for. Relative to the start of the document. >- * @param line line to cache the background color for >- */ >- void cacheLineBackground(int lineOffset, String line) { >- StyledTextEvent event = getLineBackgroundData(lineOffset, line); >- >- if (event != null) { >- lineBackgrounds.put(new Integer(lineOffset), event); >- } >- } >- /** >- * Caches all line data that needs to be requested from a listener. >- * </p> >- * @param printerContent <class>StyledTextContent</class> to request >- * line data for. >- */ >- void cacheLineData(StyledTextContent printerContent) { >- for (int i = 0; i < printerContent.getLineCount(); i++) { >- int lineOffset = printerContent.getOffsetAtLine(i); >- String line = printerContent.getLine(i); >- >- if (printOptions.printLineBackground) { >- cacheLineBackground(lineOffset, line); >- } >- if (printOptions.printTextBackground || >- printOptions.printTextForeground || >- printOptions.printTextFontStyle) { >- cacheLineStyle(lineOffset, line); >- } >- if (isBidi()) { >- cacheBidiSegments(lineOffset, line); >- } >- } >- } >- /** >- * Caches all line styles of the given line. >- * </p> >- * @param lineOffset offset of the line to cache the styles for. >- * Relative to the start of the document. >- * @param line line to cache the styles for. >- */ >- void cacheLineStyle(int lineOffset, String line) { >- StyledTextEvent event = getLineStyleData(lineOffset, line); >- >- if (event != null) { >- StyleRange[] styles = event.styles; >- for (int i = 0; i < styles.length; i++) { >- StyleRange styleCopy = null; >- if (printOptions.printTextBackground == false && styles[i].background != null) { >- styleCopy = (StyleRange) styles[i].clone(); >- styleCopy.background = null; >- } >- if (printOptions.printTextForeground == false && styles[i].foreground != null) { >- if (styleCopy == null) { >- styleCopy = (StyleRange) styles[i].clone(); >- } >- styleCopy.foreground = null; >- } >- if (printOptions.printTextFontStyle == false && styles[i].fontStyle != SWT.NORMAL) { >- if (styleCopy == null) { >- styleCopy = (StyleRange) styles[i].clone(); >- } >- styleCopy.fontStyle = SWT.NORMAL; >- } >- if (styleCopy != null) { >- styles[i] = styleCopy; >- } >- } >- lineStyles.put(new Integer(lineOffset), event); >- } >- } >- /** >- * Copies the text of the specified <class>StyledTextContent</class>. >- * </p> >- * @param original the <class>StyledTextContent</class> to copy. >- */ >- void copyContent(StyledTextContent original) { >- int insertOffset = 0; >- >- printerContent = new DefaultContent(); >- for (int i = 0; i < original.getLineCount(); i++) { >- int insertEndOffset; >- if (i < original.getLineCount() - 1) { >- insertEndOffset = original.getOffsetAtLine(i + 1); >- } >- else { >- insertEndOffset = original.getCharCount(); >- } >- printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset)); >- insertOffset = insertEndOffset; >- } >- } >- /** >- * Replaces all display colors in the cached line backgrounds and >- * line styles with printer colors. >- */ >- void createPrinterColors() { >- Enumeration values = lineBackgrounds.elements(); >- printerColors = new Hashtable(); >- while (values.hasMoreElements()) { >- StyledTextEvent event = (StyledTextEvent) values.nextElement(); >- event.lineBackground = getPrinterColor(event.lineBackground); >- } >- >- values = lineStyles.elements(); >- while (values.hasMoreElements()) { >- StyledTextEvent event = (StyledTextEvent) values.nextElement(); >- for (int i = 0; i < event.styles.length; i++) { >- StyleRange style = event.styles[i]; >- Color printerBackground = getPrinterColor(style.background); >- Color printerForeground = getPrinterColor(style.foreground); >- >- if (printerBackground != style.background || >- printerForeground != style.foreground) { >- style = (StyleRange) style.clone(); >- style.background = printerBackground; >- style.foreground = printerForeground; >- event.styles[i] = style; >- } >- } >- } >- } >- /** >- * Disposes of the resources and the <class>PrintRenderer</class>. >- */ >- void dispose() { >- if (printerColors != null) { >- Enumeration colors = printerColors.elements(); >- >- while (colors.hasMoreElements()) { >- Color color = (Color) colors.nextElement(); >- color.dispose(); >- } >- printerColors = null; >- } >- if (gc != null) { >- gc.dispose(); >- gc = null; >- } >- if (printerFont != null) { >- printerFont.dispose(); >- printerFont = null; >- } >- if (renderer != null) { >- renderer.dispose(); >- renderer = null; >- } >- } >- /** >- * Finish printing the indicated page. >- * >- * @param page page that was printed >- */ >- void endPage(int page) { >- printDecoration(page, false); >- printer.endPage(); >- } >- /** >- * Creates a <class>PrintRenderer</class> and calculate the line range >- * to print. >- */ >- void initializeRenderer() { >- Rectangle trim = printer.computeTrim(0, 0, 0, 0); >- Point dpi = printer.getDPI(); >- >- printerFont = new Font(printer, displayFontData.getName(), displayFontData.getHeight(), SWT.NORMAL); >- clientArea = printer.getClientArea(); >- pageWidth = clientArea.width; >- // one inch margin around text >- clientArea.x = dpi.x + trim.x; >- clientArea.y = dpi.y + trim.y; >- clientArea.width -= (clientArea.x + trim.width); >- clientArea.height -= (clientArea.y + trim.height); >- >- gc = new GC(printer); >- gc.setFont(printerFont); >- renderer = new PrintRenderer( >- printer, printerFont, isBidi(), gc, printerContent, >- lineBackgrounds, lineStyles, bidiSegments, >- tabLength, clientArea); >- if (printOptions.header != null) { >- int lineHeight = renderer.getLineHeight(); >- clientArea.y += lineHeight * 2; >- clientArea.height -= lineHeight * 2; >- } >- if (printOptions.footer != null) { >- clientArea.height -= renderer.getLineHeight() * 2; >- } >- pageSize = clientArea.height / renderer.getLineHeight(); >- StyledTextContent content = renderer.getContent(); >- startLine = 0; >- endLine = content.getLineCount() - 1; >- PrinterData data = printer.getPrinterData(); >- if (data.scope == PrinterData.PAGE_RANGE) { >- startLine = (startPage - 1) * pageSize; >- } else if (data.scope == PrinterData.SELECTION) { >- startLine = content.getLineAtOffset(selection.x); >- if (selection.y > 0) { >- endLine = content.getLineAtOffset(selection.x + selection.y - 1); >- } else { >- endLine = startLine - 1; >- } >- } >- } >- /** >- * Returns the printer color for the given display color. >- * </p> >- * @param color display color >- * @return color create on the printer with the same RGB values >- * as the display color. >- */ >- Color getPrinterColor(Color color) { >- Color printerColor = null; >- >- if (color != null) { >- printerColor = (Color) printerColors.get(color); >- if (printerColor == null) { >- printerColor = new Color(printer, color.getRGB()); >- printerColors.put(color, printerColor); >- } >- } >- return printerColor; >- } >- /** >- * Prints the lines in the specified page range. >- */ >- void print() { >- StyledTextContent content = renderer.getContent(); >- Color background = gc.getBackground(); >- Color foreground = gc.getForeground(); >- int lineHeight = renderer.getLineHeight(); >- int lineCount = content.getLineCount(); >- int paintY = clientArea.y; >- int page = startPage; >- >- if (singleLine) { >- lineCount = 1; >- } >- for (int i = startLine; i <= endLine && page <= endPage; i++, paintY += lineHeight) { >- String line = content.getLine(i); >- >- if (paintY == clientArea.y) { >- startPage(page); >- } >- renderer.drawLine( >- line, i, paintY, gc, background, foreground, true); >- if (paintY + lineHeight * 2 > clientArea.y + clientArea.height) { >- endPage(page); >- paintY = clientArea.y; >- page++; >- if (page > endPage || i == lineCount - 1) { >- break; >- } >- } >- } >- if (paintY > clientArea.y && paintY <= clientArea.y + clientArea.height) { >- endPage(page); >- } >- } >- /** >- * Print header or footer decorations. >- * * @param page page number to print, if specified in the StyledTextPrintOptions header or footer. * @param header true = print the header, false = print the footer */ >- void printDecoration(int page, boolean header) { >- int lastSegmentIndex = 0; >- final int SegmentCount = 3; >- String text; >- >- if (header) { >- text = printOptions.header; >- } >- else { >- text = printOptions.footer; >- } >- if (text == null) { >- return; >- } >- for (int i = 0; i < SegmentCount; i++) { >- int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex); >- String segment; >- >- if (segmentIndex == -1) { >- segment = text.substring(lastSegmentIndex); >- printDecorationSegment(segment, i, page, header); >- break; >- } >- else { >- segment = text.substring(lastSegmentIndex, segmentIndex); >- printDecorationSegment(segment, i, page, header); >- lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length(); >- } >- } >- } >- /** >- * Print one segment of a header or footer decoration. >- * Headers and footers have three different segments. >- * One each for left aligned, centered, and right aligned text. >- * >- * @param segment decoration segment to print >- * @param alignment alignment of the segment. 0=left, 1=center, 2=right >- * @param page page number to print, if specified in the decoration segment. >- * @param header true = print the header, false = print the footer >- */ >- void printDecorationSegment(String segment, int alignment, int page, boolean header) { >- int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG); >- >- if (pageIndex != -1) { >- final int PageTagLength = StyledTextPrintOptions.PAGE_TAG.length(); >- StringBuffer buffer = new StringBuffer(segment); >- buffer.replace(pageIndex, pageIndex + PageTagLength, new Integer(page).toString()); >- segment = buffer.toString(); >- } >- if (segment.length() > 0) { >- int segmentWidth; >- int drawX = 0; >- int drawY; >- StyledTextBidi bidi = null; >- >- if (isBidi()) { >- bidi = new StyledTextBidi(gc, tabLength, segment, null, null, new int[] {0, segment.length()}); >- segmentWidth = bidi.getTextWidth(); >- } >- else { >- segmentWidth = gc.textExtent(segment).x; >- } >- if (header) { >- drawY = clientArea.y - renderer.getLineHeight() * 2; >- } >- else { >- drawY = clientArea.y + clientArea.height + renderer.getLineHeight(); >- } >- if (alignment == LEFT) { >- drawX = clientArea.x; >- } >- else >- if (alignment == CENTER) { >- drawX = (pageWidth - segmentWidth) / 2; >- } >- else >- if (alignment == RIGHT) { >- drawX = clientArea.x + clientArea.width - segmentWidth; >- } >- if (bidi != null) { >- bidi.drawBidiText(0, segment.length(), drawX, drawY); >- } >- else { >- gc.drawString(segment, drawX, drawY, true); >- } >- } >- } >- /** >- * Starts a print job and prints the pages specified in the constructor. >- */ >- public void run() { >- String jobName = printOptions.jobName; >- >- if (jobName == null) { >- jobName = "Printing"; >- } >- if (printer.startJob(jobName)) { >- createPrinterColors(); >- initializeRenderer(); >- print(); >- dispose(); >- printer.endJob(); >- } >- } >- /** >- * Start printing a new page. >- * >- * @param page page number to be started >- */ >- void startPage(int page) { >- printer.startPage(); >- printDecoration(page, true); >- } >- } >- /** >- * The <code>RTFWriter</code> class is used to write widget content as >- * rich text. The implementation complies with the RTF specification >- * version 1.5. >- * <p> >- * toString() is guaranteed to return a valid RTF string only after >- * close() has been called. >- * </p> >- * <p> >- * Whole and partial lines and line breaks can be written. Lines will be >- * formatted using the styles queried from the LineStyleListener, if >- * set, or those set directly in the widget. All styles are applied to >- * the RTF stream like they are rendered by the widget. In addition, the >- * widget font name and size is used for the whole text. >- * </p> >- */ >- class RTFWriter extends TextWriter { >- final int DEFAULT_FOREGROUND = 0; >- final int DEFAULT_BACKGROUND = 1; >- Vector colorTable = new Vector(); >- boolean WriteUnicode; >- >- /** >- * Creates a RTF writer that writes content starting at offset "start" >- * in the document. <code>start</code> and <code>length</code>can be set to specify partial >- * lines. >- * <p> >- * >- * @param start start offset of content to write, 0 based from >- * beginning of document >- * @param length length of content to write >- */ >- public RTFWriter(int start, int length) { >- super(start, length); >- colorTable.addElement(getForeground()); >- colorTable.addElement(getBackground()); >- setUnicode(); >- } >- /** >- * Closes the RTF writer. Once closed no more content can be written. >- * <b>NOTE:</b> <code>toString()</code> does not return a valid RTF string until >- * <code>close()</code> has been called. >- */ >- public void close() { >- if (isClosed() == false) { >- writeHeader(); >- write("\n}}\0"); >- super.close(); >- } >- } >- /** >- * Returns the index of the specified color in the RTF color table. >- * <p> >- * >- * @param color the color >- * @param defaultIndex return value if color is null >- * @return the index of the specified color in the RTF color table >- * or "defaultIndex" if "color" is null. >- */ >- int getColorIndex(Color color, int defaultIndex) { >- int index; >- >- if (color == null) { >- index = defaultIndex; >- } >- else { >- index = colorTable.indexOf(color); >- if (index == -1) { >- index = colorTable.size(); >- colorTable.addElement(color); >- } >- } >- return index; >- } >- /** >- * Determines if Unicode RTF should be written. >- * Don't write Unicode RTF on Windows 95/98/ME or NT. >- */ >- void setUnicode() { >- final String Win95 = "windows 95"; >- final String Win98 = "windows 98"; >- final String WinME = "windows me"; >- final String WinNT = "windows nt"; >- String osName = System.getProperty("os.name").toLowerCase(); >- String osVersion = System.getProperty("os.version"); >- int majorVersion = 0; >- >- if (osName.startsWith(WinNT) && osVersion != null) { >- int majorIndex = osVersion.indexOf('.'); >- if (majorIndex != -1) { >- osVersion = osVersion.substring(0, majorIndex); >- try { >- majorVersion = Integer.parseInt(osVersion); >- } >- catch (NumberFormatException exception) { >- // ignore exception. version number remains unknown. >- // will write without Unicode >- } >- } >- } >- if (osName != null && >- osName.startsWith(Win95) == false && >- osName.startsWith(Win98) == false && >- osName.startsWith(WinME) == false && >- (osName.startsWith(WinNT) == false || majorVersion > 4)) { >- WriteUnicode = true; >- } >- else { >- WriteUnicode = false; >- } >- } >- /** >- * Appends the specified segment of "string" to the RTF data. >- * Copy from <code>start</code> up to, but excluding, <code>end</code>. >- * <p> >- * >- * @param string string to copy a segment from. Must not contain >- * line breaks. Line breaks should be written using writeLineDelimiter() >- * @param start start offset of segment. 0 based. >- * @param end end offset of segment >- */ >- void write(String string, int start, int end) { >- for (int index = start; index < end; index++) { >- char ch = string.charAt(index); >- if (ch > 0xFF && WriteUnicode) { >- // write the sub string from the last escaped character >- // to the current one. Fixes bug 21698. >- if (index > start) { >- write(string.substring(start, index)); >- } >- write("\\u"); >- write(Integer.toString((short) ch)); >- write(' '); // control word delimiter >- start = index + 1; >- } >- else >- if (ch == '}' || ch == '{' || ch == '\\') { >- // write the sub string from the last escaped character >- // to the current one. Fixes bug 21698. >- if (index > start) { >- write(string.substring(start, index)); >- } >- write('\\'); >- write(ch); >- start = index + 1; >- } >- } >- // write from the last escaped character to the end. >- // Fixes bug 21698. >- if (start < end) { >- write(string.substring(start, end)); >- } >- } >- /** >- * Writes the RTF header including font table and color table. >- */ >- void writeHeader() { >- StringBuffer header = new StringBuffer(); >- FontData fontData = getFont().getFontData()[0]; >- header.append("{\\rtf1\\ansi"); >- // specify code page, necessary for copy to work in bidi >- // systems that don't support Unicode RTF. >- String cpg = System.getProperty("file.encoding").toLowerCase(); >- if (cpg.startsWith("cp") || cpg.startsWith("ms")) { >- cpg = cpg.substring(2, cpg.length()); >- header.append("\\ansicpg"); >- header.append(cpg); >- } >- header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil "); >- header.append(fontData.getName()); >- header.append(";}}\n{\\colortbl"); >- for (int i = 0; i < colorTable.size(); i++) { >- Color color = (Color) colorTable.elementAt(i); >- header.append("\\red"); >- header.append(color.getRed()); >- header.append("\\green"); >- header.append(color.getGreen()); >- header.append("\\blue"); >- header.append(color.getBlue()); >- header.append(";"); >- } >- // some RTF readers ignore the deff0 font tag. Explicitly >- // set the font for the whole document to work around this. >- header.append("}\n{\\f0\\fs"); >- // font size is specified in half points >- header.append(fontData.getHeight() * 2); >- header.append(" "); >- write(header.toString(), 0); >- } >- /** >- * Appends the specified line text to the RTF data. Lines will be formatted >- * using the styles queried from the LineStyleListener, if set, or those set >- * directly in the widget. >- * <p> >- * >- * @param line line text to write as RTF. Must not contain line breaks >- * Line breaks should be written using writeLineDelimiter() >- * @param lineOffset offset of the line. 0 based from the start of the >- * widget document. Any text occurring before the start offset or after the >- * end offset specified during object creation is ignored. >- * @exception SWTException <ul> >- * <li>ERROR_IO when the writer is closed.</li> >- * </ul> >- */ >- public void writeLine(String line, int lineOffset) { >- StyleRange[] styles = new StyleRange[0]; >- Color lineBackground = null; >- StyledTextEvent event; >- >- if (isClosed()) { >- SWT.error(SWT.ERROR_IO); >- } >- event = renderer.getLineStyleData(lineOffset, line); >- if (event != null) { >- styles = event.styles; >- } >- event = renderer.getLineBackgroundData(lineOffset, line); >- if (event != null) { >- lineBackground = event.lineBackground; >- } >- if (lineBackground == null) { >- lineBackground = getBackground(); >- } >- writeStyledLine(line, lineOffset, styles, lineBackground); >- } >- /** >- * Appends the specified line delmimiter to the RTF data. >- * <p> >- * >- * @param lineDelimiter line delimiter to write as RTF. >- * @exception SWTException <ul> >- * <li>ERROR_IO when the writer is closed.</li> >- * </ul> >- */ >- public void writeLineDelimiter(String lineDelimiter) { >- if (isClosed()) { >- SWT.error(SWT.ERROR_IO); >- } >- write(lineDelimiter, 0, lineDelimiter.length()); >- write("\\par "); >- } >- /** >- * Appends the specified line text to the RTF data. >- * Use the colors and font styles specified in "styles" and "lineBackground". >- * Formatting is written to reflect the text rendering by the text widget. >- * Style background colors take precedence over the line background color. >- * Background colors are written using the \highlight tag (vs. the \cb tag). >- * <p> >- * >- * @param line line text to write as RTF. Must not contain line breaks >- * Line breaks should be written using writeLineDelimiter() >- * @param lineOffset offset of the line. 0 based from the start of the >- * widget document. Any text occurring before the start offset or after the >- * end offset specified during object creation is ignored. >- * @param styles styles to use for formatting. Must not be null. >- * @param linebackground line background color to use for formatting. >- * May be null. >- */ >- void writeStyledLine(String line, int lineOffset, StyleRange[] styles, Color lineBackground) { >- int lineLength = line.length(); >- int lineIndex; >- int copyEnd; >- int startOffset = getStart(); >- int endOffset = startOffset + super.getCharCount(); >- int lineEndOffset = Math.min(lineLength, endOffset - lineOffset); >- int writeOffset = startOffset - lineOffset; >- >- if (writeOffset >= line.length()) { >- return; // whole line is outside write range >- } >- else >- if (writeOffset > 0) { >- lineIndex = writeOffset; // line starts before RTF write start >- } >- else { >- lineIndex = 0; >- } >- if (lineBackground != null) { >- write("{\\highlight"); >- write(getColorIndex(lineBackground, DEFAULT_BACKGROUND)); >- write(" "); >- } >- for (int i = 0; i < styles.length; i++) { >- StyleRange style = styles[i]; >- int start = style.start - lineOffset; >- int end = start + style.length; >- int colorIndex; >- // skip over partial first line >- if (end < writeOffset) { >- continue; >- } >- // style starts beyond line end or RTF write end >- if (start >= lineEndOffset) { >- break; >- } >- // write any unstyled text >- if (lineIndex < start) { >- // copy to start of style >- // style starting betond end of write range or end of line >- // is guarded against above. >- write(line, lineIndex, start); >- lineIndex = start; >- } >- // write styled text >- colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND); >- write("{\\cf"); >- write(getColorIndex(style.foreground, DEFAULT_FOREGROUND)); >- if (colorIndex != DEFAULT_BACKGROUND) { >- write("\\highlight"); >- write(colorIndex); >- } >- if (style.fontStyle == SWT.BOLD) { >- write("\\b"); >- } >- write(" "); >- // copy to end of style or end of write range or end of line >- copyEnd = Math.min(end, lineEndOffset); >- // guard against invalid styles and let style processing continue >- copyEnd = Math.max(copyEnd, lineIndex); >- write(line, lineIndex, copyEnd); >- if (style.fontStyle == SWT.BOLD) { >- write("\\b0"); >- } >- write("}"); >- lineIndex = copyEnd; >- } >- // write unstyled text at the end of the line >- if (lineIndex < lineEndOffset) { >- write(line, lineIndex, lineEndOffset); >- } >- if (lineBackground != null) { >- write("}"); >- } >- } >- } >- /** >- * The <code>TextWriter</code> class is used to write widget content to >- * a string. Whole and partial lines and line breaks can be written. To write >- * partial lines, specify the start and length of the desired segment >- * during object creation. >- * <p> >- * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close() >- * has been called. >- */ >- class TextWriter { >- private StringBuffer buffer; >- private int startOffset; // offset of first character that will be written >- private int endOffset; // offset of last character that will be written. >- // 0 based from the beginning of the widget text. >- private boolean isClosed = false; >- >- /** >- * Creates a writer that writes content starting at offset "start" >- * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines. >- * <p> >- * >- * @param start start offset of content to write, 0 based from beginning of document >- * @param length length of content to write >- */ >- public TextWriter(int start, int length) { >- buffer = new StringBuffer(length); >- startOffset = start; >- endOffset = start + length; >- } >- /** >- * Closes the writer. Once closed no more content can be written. >- * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless >- * the writer is closed. >- */ >- public void close() { >- if (isClosed == false) { >- isClosed = true; >- } >- } >- /** >- * Returns the number of characters to write. >- */ >- public int getCharCount() { >- return endOffset - startOffset; >- } >- /** >- * Returns the offset where writing starts. 0 based from the start of >- * the widget text. Used to write partial lines. >- */ >- public int getStart() { >- return startOffset; >- } >- /** >- * Returns whether the writer is closed. >- */ >- public boolean isClosed() { >- return isClosed; >- } >- /** >- * Returns the string. <code>close()</code> must be called before <code>toString()</code> >- * is guaranteed to return a valid string. >- * <p> >- * >- * @return the string >- */ >- public String toString() { >- return buffer.toString(); >- } >- /** >- * Appends the given string to the data. >- */ >- void write(String string) { >- buffer.append(string); >- } >- /** >- * Inserts the given string to the data at the specified offset. >- * Do nothing if "offset" is < 0 or > getCharCount() >- * <p> >- * >- * @param string text to insert >- * @param offset offset in the existing data to insert "string" at. >- */ >- void write(String string, int offset) { >- if (offset < 0 || offset > buffer.length()) { >- return; >- } >- buffer.insert(offset, string); >- } >- /** >- * Appends the given int to the data. >- */ >- void write(int i) { >- buffer.append(i); >- } >- /** >- * Appends the given character to the data. >- */ >- void write(char i) { >- buffer.append(i); >- } >- /** >- * Appends the specified line text to the data. >- * <p> >- * >- * @param line line text to write. Must not contain line breaks >- * Line breaks should be written using writeLineDelimiter() >- * @param lineOffset offset of the line. 0 based from the start of the >- * widget document. Any text occurring before the start offset or after the >- * end offset specified during object creation is ignored. >- * @exception SWTException <ul> >- * <li>ERROR_IO when the writer is closed.</li> >- * </ul> >- */ >- public void writeLine(String line, int lineOffset) { >- int lineLength = line.length(); >- int lineIndex; >- int copyEnd; >- int writeOffset = startOffset - lineOffset; >- >- if (isClosed) { >- SWT.error(SWT.ERROR_IO); >- } >- if (writeOffset >= lineLength) { >- return; // whole line is outside write range >- } >- else >- if (writeOffset > 0) { >- lineIndex = writeOffset; // line starts before write start >- } >- else { >- lineIndex = 0; >- } >- copyEnd = Math.min(lineLength, endOffset - lineOffset); >- if (lineIndex < copyEnd) { >- write(line.substring(lineIndex, copyEnd)); >- } >- } >- /** >- * Appends the specified line delmimiter to the data. >- * <p> >- * >- * @param lineDelimiter line delimiter to write >- * @exception SWTException <ul> >- * <li>ERROR_IO when the writer is closed.</li> >- * </ul> >- */ >- public void writeLineDelimiter(String lineDelimiter) { >- if (isClosed) { >- SWT.error(SWT.ERROR_IO); >- } >- write(lineDelimiter); >- } >- } >- /** >- * LineCache provides an interface to calculate and invalidate >- * line based data. >- * Implementors need to return a line width in <code>getWidth</code>. >- */ >- interface LineCache { >- /** >- * Calculates the lines in the specified range. >- * <p> >- * >- * @param startLine first line to calculate >- * @param lineCount number of lines to calculate >- */ >- public void calculate(int startLine, int lineCount); >- /** >- * Returns a width that will be used by the <code>StyledText</code> >- * widget to size a horizontal scroll bar. >- * <p> >- * >- * @return the line width >- */ >- public int getWidth(); >- /** >- * Resets the lines in the specified range. >- * This method is called in <code>StyledText.redraw()</code> >- * and allows implementors to call redraw themselves during reset. >- * <p> >- * >- * @param startLine the first line to reset >- * @param lineCount the number of lines to reset >- * @param calculateMaxWidth true=implementors should retain a >- * valid width even if it is affected by the reset operation. >- * false=the width may be set to 0 >- */ >- public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth); >- /** >- * Resets the lines in the specified range. >- * <p> >- * >- * @param startLine the first line to reset >- * @param lineCount the number of lines to reset >- * @param calculateMaxWidth true=implementors should retain a >- * valid width even if it is affected by the reset operation. >- * false=the width may be set to 0 >- */ >- public void reset(int startLine, int lineCount, boolean calculateMaxWidth); >- /** >- * Called when a text change occurred. >- * <p> >- * >- * @param startOffset the start offset of the text change >- * @param newLineCount the number of inserted lines >- * @param replaceLineCount the number of deleted lines >- * @param newCharCount the number of new characters >- * @param replaceCharCount the number of deleted characters >- */ >- public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount); >- } >- /** >- * Keeps track of line widths and the longest line in the >- * StyledText document. >- * Line widths are calculated when requested by a call to >- * <code>calculate</code> and cached until reset by a call >- * to <code>redrawReset</code> or <code>reset</code>. >- */ >- class ContentWidthCache implements LineCache { >- StyledText parent; // parent widget, used to create a GC for line measuring >- int[] lineWidth; // width in pixel of each line in the document, -1 for unknown width >- int lineCount; // number of lines in lineWidth array >- int maxWidth; // maximum line width of all measured lines >- int maxWidthLineIndex; // index of the widest line >- >- /** >- * Creates a new <code>ContentWidthCache</code> and allocates space >- * for the given number of lines. >- * <p> >- * >- * @param parent the StyledText widget used to create a GC for >- * line measuring >- * @param lineCount initial number of lines to allocate space for >- */ >- public ContentWidthCache(StyledText parent, int lineCount) { >- this.lineCount = lineCount; >- this.parent = parent; >- lineWidth = new int[lineCount]; >- reset(0, lineCount, false); >- } >- /** >- * Calculates the width of each line in the given range if it has >- * not been calculated yet. >- * If any line in the given range is wider than the currently widest >- * line, the maximum line width is updated, >- * <p> >- * >- * @param startLine first line to calculate the line width of >- * @param lineCount number of lines to calculate the line width for >- */ >- public void calculate(int startLine, int lineCount) { >- GC gc = null; >- int caretWidth = 0; >- int stopLine = startLine + lineCount; >- >- for (int i = startLine; i < stopLine; i++) { >- if (lineWidth[i] == -1) { >- String line = content.getLine(i); >- int lineOffset = content.getOffsetAtLine(i); >- >- if (gc == null) { >- gc = parent.getGC(); >- caretWidth = getCaretWidth(); >- } >- lineWidth[i] = contentWidth(line, lineOffset, gc) + caretWidth; >- } >- if (lineWidth[i] > maxWidth) { >- maxWidth = lineWidth[i]; >- maxWidthLineIndex = i; >- } >- } >- if (gc != null) { >- gc.dispose(); >- } >- } >- /** >- * Calculates the width of the visible lines in the specified >- * range. >- * <p> >- * >- * @param startLine the first changed line >- * @param newLineCount the number of inserted lines >- */ >- void calculateVisible(int startLine, int newLineCount) { >- int topIndex = parent.getTopIndex(); >- int bottomLine = Math.min(getPartialBottomIndex(), startLine + newLineCount); >- >- startLine = Math.max(startLine, topIndex); >- calculate(startLine, bottomLine - startLine + 1); >- } >- /** >- * Measures the width of the given line. >- * <p> >- * >- * @param line the line to measure >- * @param lineOffset start offset of the line to measure, relative >- * to the start of the document >- * @param gc the GC to use for measuring the line >- * @param currentFont the font currently set in gc. Cached for better >- * performance. Null when running in a bidi locale. >- * @return the width of the given line >- */ >- int contentWidth(String line, int lineOffset, GC gc) { >- int width; >- >- if (isBidi()) { >- StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc); >- width = bidi.getTextWidth(); >- } >- else { >- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >- StyleRange[] styles = null; >- if (event != null) { >- styles = renderer.filterLineStyles(event.styles); >- } >- width = renderer.getTextWidth(line, lineOffset, 0, line.length(), styles, 0, gc); >- } >- return width + leftMargin; >- } >- /** >- * Grows the <code>lineWidth</code> array to accomodate new line width >- * information. >- * <p> >- * >- * @param numLines the number of elements to increase the array by >- */ >- void expandLines(int numLines) { >- int size = lineWidth.length; >- if (size - lineCount >= numLines) { >- return; >- } >- int[] newLines = new int[Math.max(size * 2, size + numLines)]; >- System.arraycopy(lineWidth, 0, newLines, 0, size); >- lineWidth = newLines; >- reset(size, lineWidth.length - size, false); >- } >- /** >- * Returns the width of the longest measured line. >- * <p> >- * >- * @return the width of the longest measured line. >- */ >- public int getWidth() { >- return maxWidth; >- } >- /** >- * Updates the line width array to reflect inserted or deleted lines. >- * <p> >- * >- * @param start the starting line of the change that took place >- * @param delta the number of lines in the change, > 0 indicates lines inserted, >- * < 0 indicates lines deleted >- */ >- void linesChanged(int startLine, int delta) { >- boolean inserting = delta > 0; >- >- if (delta == 0) { >- return; >- } >- if (inserting) { >- // shift the lines down to make room for new lines >- expandLines(delta); >- for (int i = lineCount - 1; i >= startLine; i--) { >- lineWidth[i + delta] = lineWidth[i]; >- } >- // reset the new lines >- for (int i = startLine + 1; i <= startLine + delta && i < lineWidth.length; i++) { >- lineWidth[i] = -1; >- } >- // have new lines been inserted above the longest line? >- if (maxWidthLineIndex >= startLine) { >- maxWidthLineIndex += delta; >- } >- } >- else { >- // shift up the lines >- for (int i = startLine - delta; i < lineCount; i++) { >- lineWidth[i+delta] = lineWidth[i]; >- } >- // has the longest line been removed? >- if (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine - delta) { >- maxWidth = 0; >- maxWidthLineIndex = -1; >- } >- else >- if (maxWidthLineIndex >= startLine - delta) { >- maxWidthLineIndex += delta; >- } >- } >- lineCount += delta; >- } >- /** >- * Resets the line width of the lines in the specified range. >- * <p> >- * >- * @param startLine the first line to reset >- * @param lineCount the number of lines to reset >- * @param calculateMaxWidth true=if the widest line is being >- * reset the maximum width of all remaining cached lines is >- * calculated. false=the maximum width is set to 0 if the >- * widest line is being reset. >- */ >- public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) { >- reset(startLine, lineCount, calculateMaxWidth); >- } >- /** >- * Resets the line width of the lines in the specified range. >- * <p> >- * >- * @param startLine the first line to reset >- * @param lineCount the number of lines to reset >- * @param calculateMaxWidth true=if the widest line is being >- * reset the maximum width of all remaining cached lines is >- * calculated. false=the maximum width is set to 0 if the >- * widest line is being reset. >- */ >- public void reset(int startLine, int lineCount, boolean calculateMaxWidth) { >- int endLine = startLine + lineCount; >- >- if (startLine < 0 || endLine > lineWidth.length) { >- return; >- } >- for (int i = startLine; i < endLine; i++) { >- lineWidth[i] = -1; >- } >- // if the longest line is one of the reset lines, the maximum line >- // width is no longer valid >- if (maxWidthLineIndex >= startLine && maxWidthLineIndex < endLine) { >- maxWidth = 0; >- maxWidthLineIndex = -1; >- if (calculateMaxWidth) { >- for (int i = 0; i < lineCount; i++) { >- if (lineWidth[i] > maxWidth) { >- maxWidth = lineWidth[i]; >- maxWidthLineIndex = i; >- } >- } >- } >- } >- } >- /** >- * Updates the line width array to reflect a text change. >- * Lines affected by the text change will be reset. >- * <p> >- * >- * @param startOffset the start offset of the text change >- * @param newLineCount the number of inserted lines >- * @param replaceLineCount the number of deleted lines >- * @param newCharCount the number of new characters >- * @param replaceCharCount the number of deleted characters >- */ >- public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) { >- int startLine = parent.getLineAtOffset(startOffset); >- boolean removedMaxLine = (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine + replaceLineCount); >- // entire text deleted? >- if (startLine == 0 && replaceLineCount == lineCount) { >- lineCount = newLineCount; >- lineWidth = new int[lineCount]; >- reset(0, lineCount, false); >- maxWidth = 0; >- } >- else { >- linesChanged(startLine, -replaceLineCount); >- linesChanged(startLine, newLineCount); >- lineWidth[startLine] = -1; >- } >- // only calculate the visible lines. otherwise measurements of changed lines >- // outside the visible area may subsequently change again without the >- // lines ever being visible. >- calculateVisible(startLine, newLineCount); >- // maxWidthLineIndex will be -1 (i.e., unknown line width) if the widget has >- // not been visible yet and the changed lines have therefore not been >- // calculated above. >- if (removedMaxLine || >- (maxWidthLineIndex != -1 && lineWidth[maxWidthLineIndex] < maxWidth)) { >- // longest line has been removed or changed and is now shorter. >- // need to recalculate maximum content width for all lines >- maxWidth = 0; >- for (int i = 0; i < lineCount; i++) { >- if (lineWidth[i] > maxWidth) { >- maxWidth = lineWidth[i]; >- maxWidthLineIndex = i; >- } >- } >- } >- } >- } >- /** >- * Updates the line wrapping of the content. >- * The line wrapping must always be in a consistent state. >- * Therefore, when <code>reset</code> or <code>redrawReset</code> >- * is called, the line wrapping is recalculated immediately >- * instead of in <code>calculate</code>. >- */ >- class WordWrapCache implements LineCache { >- StyledText parent; >- WrappedContent visualContent; >- >- /** >- * Creates a new <code>WordWrapCache</code> and calculates an initial >- * line wrapping. >- * <p> >- * >- * @param parent the StyledText widget to wrap content in. >- * @param content the content provider that does the actual line wrapping. >- */ >- public WordWrapCache(StyledText parent, WrappedContent content) { >- this.parent = parent; >- visualContent = content; >- visualContent.wrapLines(); >- } >- /** >- * Do nothing. Lines are wrapped immediately after reset. >- * <p> >- * >- * @param startLine first line to calculate >- * @param lineCount number of lines to calculate >- */ >- public void calculate(int startLine, int lineCount) { >- } >- /** >- * Returns the client area width. Lines are wrapped so there >- * is no horizontal scroll bar. >- * <p> >- * >- * @return the line width >- */ >- public int getWidth() { >- return parent.getClientArea().width; >- } >- /** >- * Wraps the lines in the specified range. >- * This method is called in <code>StyledText.redraw()</code>. >- * A redraw is therefore not necessary. >- * <p> >- * >- * @param startLine the first line to reset >- * @param lineCount the number of lines to reset >- * @param calculateMaxWidth true=implementors should retain a >- * valid width even if it is affected by the reset operation. >- * false=the width may be set to 0 >- */ >- public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) { >- if (lineCount == visualContent.getLineCount()) { >- // do a full rewrap if all lines are reset >- visualContent.wrapLines(); >- } >- else { >- visualContent.reset(startLine, lineCount); >- } >- } >- /** >- * Rewraps the lines in the specified range and redraws >- * the widget if the line wrapping has changed. >- * <p> >- * >- * @param startLine the first line to reset >- * @param lineCount the number of lines to reset >- * @param calculateMaxWidth true=implementors should retain a >- * valid width even if it is affected by the reset operation. >- * false=the width may be set to 0 >- */ >- public void reset(int startLine, int lineCount, boolean calculateMaxWidth) { >- int itemCount = getPartialBottomIndex() - topIndex + 1; >- int[] oldLineOffsets = new int[itemCount]; >- >- for (int i = 0; i < itemCount; i++) { >- oldLineOffsets[i] = visualContent.getOffsetAtLine(i + topIndex); >- } >- redrawReset(startLine, lineCount, calculateMaxWidth); >- // check for cases which will require a full redraw >- if (getPartialBottomIndex() - topIndex + 1 != itemCount) { >- // number of visible lines has changed >- parent.internalRedraw(); >- } >- else { >- for (int i = 0; i < itemCount; i++) { >- if (visualContent.getOffsetAtLine(i + topIndex) != oldLineOffsets[i]) { >- // wrapping of one of the visible lines has changed >- parent.internalRedraw(); >- break; >- } >- } >- } >- } >- /** >- * Passes the text change notification to the line wrap content. >- * <p> >- * >- * @param startOffset the start offset of the text change >- * @param newLineCount the number of inserted lines >- * @param replaceLineCount the number of deleted lines >- * @param newCharCount the number of new characters >- * @param replaceCharCount the number of deleted characters >- */ >- public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) { >- int startLine = visualContent.getLineAtOffset(startOffset); >- >- visualContent.textChanged(startOffset, newLineCount, replaceLineCount, newCharCount, replaceCharCount); >- if (startLine <= getPartialBottomIndex()) { >- // only redraw if the text change is inside or above the >- // visible lines. if it is below the visible lines it will >- // not affect the word wrapping. fixes bug 14047. >- parent.internalRedraw(); >- } >- } >- } >+ static final char TAB = '\t'; >+ static final String PlatformLineDelimiter = System.getProperty("line.separator"); >+ static final int BIDI_CARET_WIDTH = 4; >+ static final int XINSET = BIDI_CARET_WIDTH - 1; >+ static final int DEFAULT_WIDTH = 64; >+ static final int DEFAULT_HEIGHT = 64; >+ >+ static final int ExtendedModify = 3000; >+ static final int LineGetBackground = 3001; >+ static final int LineGetStyle = 3002; >+ static final int TextChanging = 3003; >+ static final int TextSet = 3004; >+ static final int VerifyKey = 3005; >+ static final int TextChanged = 3006; >+ static final int LineGetSegments = 3007; >+ >+ Color selectionBackground; // selection background color >+ Color selectionForeground; // selection foreground color >+ StyledTextContent logicalContent; // native content (default or user specified) >+ StyledTextContent content; // line wrapping content, same as logicalContent if word wrap is off >+ DisplayRenderer renderer; >+ TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent >+ DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered >+ LineCache lineCache; >+ boolean userLineStyle = false; // true=widget is using a user defined line style listener for line styles. false=widget is using the default line styler to store line styles >+ boolean userLineBackground = false; // true=widget is using a user defined line background listener for line backgrounds. false=widget is using the default line styler to store line backgrounds >+ int verticalScrollOffset = 0; // pixel based >+ int horizontalScrollOffset = 0; // pixel based >+ int topIndex = 0; // top visible line >+ int topOffset = 0; // offset of first character in top line >+ int clientAreaHeight = 0; // the client area height. Needed to calculate content width for new >+ // visible lines during Resize callback >+ int clientAreaWidth = 0; // the client area width. Needed during Resize callback to determine >+ // if line wrap needs to be recalculated >+ int lineHeight; // line height=font height >+ int tabLength = 4; // number of characters in a tab >+ int lineEndSpaceWidth; // space, in pixel, used to indicated a selected line break >+ int leftMargin = 1; >+ int topMargin = 1; >+ int rightMargin = 2; >+ int bottomMargin = 2; >+ Cursor ibeamCursor; >+ Cursor arrowCursor; >+ Cursor currentCursor; // tracks the current mouse pointer >+ Cursor customCursor = null; >+ int columnX; // keep track of the horizontal caret position >+ // when changing lines/pages. Fixes bug 5935 >+ int caretOffset = 0; >+ Point selection = new Point(0, 0); // x is the beginning character offset, y is the ending character offset >+ int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text >+ Point doubleClickSelection; // selection after last mouse double click >+ boolean editable = true; >+ boolean wordWrap = false; >+ boolean doubleClickEnabled = true; // see getDoubleClickEnabled >+ boolean overwrite = false; // insert/overwrite edit mode >+ int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default. >+ Hashtable keyActionMap = new Hashtable(); >+ Color background = null; // workaround for bug 4791 >+ Color foreground = null; // >+ Clipboard clipboard; >+ boolean mouseDoubleClick = false; // true=a double click ocurred. Don't do mouse swipe selection. >+ int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left) >+ int lastTextChangeStart; // cache data of the >+ int lastTextChangeNewLineCount; // last text changing >+ int lastTextChangeNewCharCount; // event for use in the >+ int lastTextChangeReplaceLineCount; // text changed handler >+ int lastTextChangeReplaceCharCount; >+ boolean isBidi; >+ boolean bidiColoring = false; // apply the BIDI algorithm on text segments of the same color >+ Image leftCaretBitmap = null; >+ Image rightCaretBitmap = null; >+ int caretDirection = SWT.NULL; >+ PaletteData caretPalette = null; >+ int lastCaretDirection = SWT.NULL; >+ boolean isCarbon; // flag set to true on Mac OSX >+ >+ /** >+ * The Printing class implements printing of a range of text. >+ * An instance of <class>Printing </class> is returned in the >+ * StyledText#print(Printer) API. The run() method may be >+ * invoked from any thread. >+ */ >+ class Printing implements Runnable { >+ final static int LEFT = 0; // left aligned header/footer segment >+ final static int CENTER = 1; // centered header/footer segment >+ final static int RIGHT = 2; // right aligned header/footer segment >+ >+ Printer printer; >+ PrintRenderer renderer; >+ StyledTextPrintOptions printOptions; >+ StyledTextContent printerContent; // copy of the widget content >+ Rectangle clientArea; // client area to print on >+ Font printerFont; >+ FontData displayFontData; >+ Hashtable printerColors; // printer color cache for line backgrounds and style >+ Hashtable lineBackgrounds = new Hashtable(); // cached line backgrounds >+ Hashtable lineStyles = new Hashtable(); // cached line styles >+ Hashtable bidiSegments = new Hashtable(); // cached bidi segments when running on a bidi platform >+ GC gc; // printer GC >+ int pageWidth; // width of a printer page in pixels >+ int startPage; // first page to print >+ int endPage; // last page to print >+ int pageSize; // number of lines on a page >+ int startLine; // first (wrapped) line to print >+ int endLine; // last (wrapped) line to print >+ boolean singleLine; // widget single line mode >+ Point selection = null; // selected text >+ >+ /** >+ * Creates an instance of <class>Printing</class>. >+ * Copies the widget content and rendering data that needs >+ * to be requested from listeners. >+ * </p> >+ * @param parent StyledText widget to print. >+ * @param printer printer device to print on. >+ * @param printOptions print options >+ */ >+ Printing(StyledText parent, Printer printer, StyledTextPrintOptions printOptions) { >+ PrinterData data = printer.getPrinterData(); >+ >+ this.printer = printer; >+ this.printOptions = printOptions; >+ singleLine = parent.isSingleLine(); >+ startPage = 1; >+ endPage = Integer.MAX_VALUE; >+ if (data.scope == PrinterData.PAGE_RANGE) { >+ startPage = data.startPage; >+ endPage = data.endPage; >+ if (endPage < startPage) { >+ int temp = endPage; >+ endPage = startPage; >+ startPage = temp; >+ } >+ } if (data.scope == PrinterData.SELECTION) { >+ selection = parent.getSelectionRange(); >+ } >+ >+ displayFontData = getFont().getFontData()[0]; >+ copyContent(parent.getContent()); >+ cacheLineData(printerContent); >+ } >+ /** >+ * Caches the bidi segments of the given line. >+ * </p> >+ * @param lineOffset offset of the line to cache bidi segments for. >+ * Relative to the start of the document. >+ * @param line line to cache bidi segments for. >+ */ >+ void cacheBidiSegments(int lineOffset, String line) { >+ int[] segments = getBidiSegments(lineOffset, line); >+ >+ if (segments != null) { >+ bidiSegments.put(new Integer(lineOffset), segments); >+ } >+ } >+ /** >+ * Caches the line background color of the given line. >+ * </p> >+ * @param lineOffset offset of the line to cache the background >+ * color for. Relative to the start of the document. >+ * @param line line to cache the background color for >+ */ >+ void cacheLineBackground(int lineOffset, String line) { >+ StyledTextEvent event = getLineBackgroundData(lineOffset, line); >+ >+ if (event != null) { >+ lineBackgrounds.put(new Integer(lineOffset), event); >+ } >+ } >+ /** >+ * Caches all line data that needs to be requested from a listener. >+ * </p> >+ * @param printerContent <class>StyledTextContent</class> to request >+ * line data for. >+ */ >+ void cacheLineData(StyledTextContent printerContent) { >+ for (int i = 0; i < printerContent.getLineCount(); i++) { >+ int lineOffset = printerContent.getOffsetAtLine(i); >+ String line = printerContent.getLine(i); >+ >+ if (printOptions.printLineBackground) { >+ cacheLineBackground(lineOffset, line); >+ } >+ if (printOptions.printTextBackground || >+ printOptions.printTextForeground || >+ printOptions.printTextFontStyle) { >+ cacheLineStyle(lineOffset, line); >+ } >+ if (isBidi()) { >+ cacheBidiSegments(lineOffset, line); >+ } >+ } >+ } >+ /** >+ * Caches all line styles of the given line. >+ * </p> >+ * @param lineOffset offset of the line to cache the styles for. >+ * Relative to the start of the document. >+ * @param line line to cache the styles for. >+ */ >+ void cacheLineStyle(int lineOffset, String line) { >+ StyledTextEvent event = getLineStyleData(lineOffset, line); >+ >+ if (event != null) { >+ StyleRange[] styles = event.styles; >+ for (int i = 0; i < styles.length; i++) { >+ StyleRange styleCopy = null; >+ if (printOptions.printTextBackground == false && styles[i].background != null) { >+ styleCopy = (StyleRange) styles[i].clone(); >+ styleCopy.background = null; >+ } >+ if (printOptions.printTextForeground == false && styles[i].foreground != null) { >+ if (styleCopy == null) { >+ styleCopy = (StyleRange) styles[i].clone(); >+ } >+ styleCopy.foreground = null; >+ } >+ if (printOptions.printTextFontStyle == false && styles[i].fontStyle != SWT.NORMAL) { >+ if (styleCopy == null) { >+ styleCopy = (StyleRange) styles[i].clone(); >+ } >+ styleCopy.fontStyle = SWT.NORMAL; >+ } >+ if (styleCopy != null) { >+ styles[i] = styleCopy; >+ } >+ } >+ lineStyles.put(new Integer(lineOffset), event); >+ } >+ } >+ /** >+ * Copies the text of the specified <class>StyledTextContent</class>. >+ * </p> >+ * @param original the <class>StyledTextContent</class> to copy. >+ */ >+ void copyContent(StyledTextContent original) { >+ int insertOffset = 0; >+ >+ printerContent = new DefaultContent(); >+ for (int i = 0; i < original.getLineCount(); i++) { >+ int insertEndOffset; >+ if (i < original.getLineCount() - 1) { >+ insertEndOffset = original.getOffsetAtLine(i + 1); >+ } >+ else { >+ insertEndOffset = original.getCharCount(); >+ } >+ printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset)); >+ insertOffset = insertEndOffset; >+ } >+ } >+ /** >+ * Replaces all display colors in the cached line backgrounds and >+ * line styles with printer colors. >+ */ >+ void createPrinterColors() { >+ Enumeration values = lineBackgrounds.elements(); >+ printerColors = new Hashtable(); >+ while (values.hasMoreElements()) { >+ StyledTextEvent event = (StyledTextEvent) values.nextElement(); >+ event.lineBackground = getPrinterColor(event.lineBackground); >+ } >+ >+ values = lineStyles.elements(); >+ while (values.hasMoreElements()) { >+ StyledTextEvent event = (StyledTextEvent) values.nextElement(); >+ for (int i = 0; i < event.styles.length; i++) { >+ StyleRange style = event.styles[i]; >+ Color printerBackground = getPrinterColor(style.background); >+ Color printerForeground = getPrinterColor(style.foreground); >+ >+ if (printerBackground != style.background || >+ printerForeground != style.foreground) { >+ style = (StyleRange) style.clone(); >+ style.background = printerBackground; >+ style.foreground = printerForeground; >+ event.styles[i] = style; >+ } >+ } >+ } >+ } >+ /** >+ * Disposes of the resources and the <class>PrintRenderer</class>. >+ */ >+ void dispose() { >+ if (printerColors != null) { >+ Enumeration colors = printerColors.elements(); >+ >+ while (colors.hasMoreElements()) { >+ Color color = (Color) colors.nextElement(); >+ color.dispose(); >+ } >+ printerColors = null; >+ } >+ if (gc != null) { >+ gc.dispose(); >+ gc = null; >+ } >+ if (printerFont != null) { >+ printerFont.dispose(); >+ printerFont = null; >+ } >+ if (renderer != null) { >+ renderer.dispose(); >+ renderer = null; >+ } >+ } >+ /** >+ * Finish printing the indicated page. >+ * >+ * @param page page that was printed >+ */ >+ void endPage(int page) { >+ printDecoration(page, false); >+ printer.endPage(); >+ } >+ /** >+ * Creates a <class>PrintRenderer</class> and calculate the line range >+ * to print. >+ */ >+ void initializeRenderer() { >+ Rectangle trim = printer.computeTrim(0, 0, 0, 0); >+ Point dpi = printer.getDPI(); >+ >+ printerFont = new Font(printer, displayFontData.getName(), displayFontData.getHeight(), SWT.NORMAL); >+ clientArea = printer.getClientArea(); >+ pageWidth = clientArea.width; >+ // one inch margin around text >+ clientArea.x = dpi.x + trim.x; >+ clientArea.y = dpi.y + trim.y; >+ clientArea.width -= (clientArea.x + trim.width); >+ clientArea.height -= (clientArea.y + trim.height); >+ >+ gc = new GC(printer); >+ gc.setFont(printerFont); >+ renderer = new PrintRenderer( >+ printer, printerFont, isBidi(), gc, printerContent, >+ lineBackgrounds, lineStyles, bidiSegments, >+ tabLength, clientArea); >+ if (printOptions.header != null) { >+ int lineHeight = renderer.getLineHeight(); >+ clientArea.y += lineHeight * 2; >+ clientArea.height -= lineHeight * 2; >+ } >+ if (printOptions.footer != null) { >+ clientArea.height -= renderer.getLineHeight() * 2; >+ } >+ pageSize = clientArea.height / renderer.getLineHeight(); >+ StyledTextContent content = renderer.getContent(); >+ startLine = 0; >+ endLine = content.getLineCount() - 1; >+ PrinterData data = printer.getPrinterData(); >+ if (data.scope == PrinterData.PAGE_RANGE) { >+ startLine = (startPage - 1) * pageSize; >+ } else if (data.scope == PrinterData.SELECTION) { >+ startLine = content.getLineAtOffset(selection.x); >+ if (selection.y > 0) { >+ endLine = content.getLineAtOffset(selection.x + selection.y - 1); >+ } else { >+ endLine = startLine - 1; >+ } >+ } >+ } >+ /** >+ * Returns the printer color for the given display color. >+ * </p> >+ * @param color display color >+ * @return color create on the printer with the same RGB values >+ * as the display color. >+ */ >+ Color getPrinterColor(Color color) { >+ Color printerColor = null; >+ >+ if (color != null) { >+ printerColor = (Color) printerColors.get(color); >+ if (printerColor == null) { >+ printerColor = new Color(printer, color.getRGB()); >+ printerColors.put(color, printerColor); >+ } >+ } >+ return printerColor; >+ } >+ /** >+ * Prints the lines in the specified page range. >+ */ >+ void print() { >+ StyledTextContent content = renderer.getContent(); >+ Color background = gc.getBackground(); >+ Color foreground = gc.getForeground(); >+ int lineHeight = renderer.getLineHeight(); >+ int lineCount = content.getLineCount(); >+ int paintY = clientArea.y; >+ int page = startPage; >+ >+ if (singleLine) { >+ lineCount = 1; >+ } >+ for (int i = startLine; i <= endLine && page <= endPage; i++, paintY += lineHeight) { >+ String line = content.getLine(i); >+ >+ if (paintY == clientArea.y) { >+ startPage(page); >+ } >+ renderer.drawLine( >+ line, i, paintY, gc, background, foreground, true); >+ if (paintY + lineHeight * 2 > clientArea.y + clientArea.height) { >+ endPage(page); >+ paintY = clientArea.y; >+ page++; >+ if (page > endPage || i == lineCount - 1) { >+ break; >+ } >+ } >+ } >+ if (paintY > clientArea.y && paintY <= clientArea.y + clientArea.height) { >+ endPage(page); >+ } >+ } >+ /** >+ * Print header or footer decorations. >+ * >+ * @param page page number to print, if specified in the StyledTextPrintOptions header or footer. >+ * @param header true = print the header, false = print the footer >+ */ >+ void printDecoration(int page, boolean header) { >+ int lastSegmentIndex = 0; >+ final int SegmentCount = 3; >+ String text; >+ >+ if (header) { >+ text = printOptions.header; >+ } >+ else { >+ text = printOptions.footer; >+ } >+ if (text == null) { >+ return; >+ } >+ for (int i = 0; i < SegmentCount; i++) { >+ int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex); >+ String segment; >+ >+ if (segmentIndex == -1) { >+ segment = text.substring(lastSegmentIndex); >+ printDecorationSegment(segment, i, page, header); >+ break; >+ } >+ else { >+ segment = text.substring(lastSegmentIndex, segmentIndex); >+ printDecorationSegment(segment, i, page, header); >+ lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length(); >+ } >+ } >+ } >+ /** >+ * Print one segment of a header or footer decoration. >+ * Headers and footers have three different segments. >+ * One each for left aligned, centered, and right aligned text. >+ * >+ * @param segment decoration segment to print >+ * @param alignment alignment of the segment. 0=left, 1=center, 2=right >+ * @param page page number to print, if specified in the decoration segment. >+ * @param header true = print the header, false = print the footer >+ */ >+ void printDecorationSegment(String segment, int alignment, int page, boolean header) { >+ int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG); >+ >+ if (pageIndex != -1) { >+ final int PageTagLength = StyledTextPrintOptions.PAGE_TAG.length(); >+ StringBuffer buffer = new StringBuffer(segment); >+ buffer.replace(pageIndex, pageIndex + PageTagLength, new Integer(page).toString()); >+ segment = buffer.toString(); >+ } >+ if (segment.length() > 0) { >+ int segmentWidth; >+ int drawX = 0; >+ int drawY; >+ StyledTextBidi bidi = null; >+ >+ if (isBidi()) { >+ bidi = new StyledTextBidi(gc, tabLength, segment, null, null, new int[] {0, segment.length()}); >+ segmentWidth = bidi.getTextWidth(); >+ } >+ else { >+ segmentWidth = gc.textExtent(segment).x; >+ } >+ if (header) { >+ drawY = clientArea.y - renderer.getLineHeight() * 2; >+ } >+ else { >+ drawY = clientArea.y + clientArea.height + renderer.getLineHeight(); >+ } >+ if (alignment == LEFT) { >+ drawX = clientArea.x; >+ } >+ else >+ if (alignment == CENTER) { >+ drawX = (pageWidth - segmentWidth) / 2; >+ } >+ else >+ if (alignment == RIGHT) { >+ drawX = clientArea.x + clientArea.width - segmentWidth; >+ } >+ if (bidi != null) { >+ bidi.drawBidiText(0, segment.length(), drawX, drawY); >+ } >+ else { >+ gc.drawString(segment, drawX, drawY, true); >+ } >+ } >+ } >+ /** >+ * Starts a print job and prints the pages specified in the constructor. >+ */ >+ public void run() { >+ String jobName = printOptions.jobName; >+ >+ if (jobName == null) { >+ jobName = "Printing"; >+ } >+ if (printer.startJob(jobName)) { >+ createPrinterColors(); >+ initializeRenderer(); >+ print(); >+ dispose(); >+ printer.endJob(); >+ } >+ } >+ /** >+ * Start printing a new page. >+ * >+ * @param page page number to be started >+ */ >+ void startPage(int page) { >+ printer.startPage(); >+ printDecoration(page, true); >+ } >+ } >+ /** >+ * The <code>RTFWriter</code> class is used to write widget content as >+ * rich text. The implementation complies with the RTF specification >+ * version 1.5. >+ * <p> >+ * toString() is guaranteed to return a valid RTF string only after >+ * close() has been called. >+ * </p> >+ * <p> >+ * Whole and partial lines and line breaks can be written. Lines will be >+ * formatted using the styles queried from the LineStyleListener, if >+ * set, or those set directly in the widget. All styles are applied to >+ * the RTF stream like they are rendered by the widget. In addition, the >+ * widget font name and size is used for the whole text. >+ * </p> >+ */ >+ class RTFWriter extends TextWriter { >+ final int DEFAULT_FOREGROUND = 0; >+ final int DEFAULT_BACKGROUND = 1; >+ Vector colorTable = new Vector(); >+ boolean WriteUnicode; >+ >+ /** >+ * Creates a RTF writer that writes content starting at offset "start" >+ * in the document. <code>start</code> and <code>length</code>can be set to specify partial >+ * lines. >+ * <p> >+ * >+ * @param start start offset of content to write, 0 based from >+ * beginning of document >+ * @param length length of content to write >+ */ >+ public RTFWriter(int start, int length) { >+ super(start, length); >+ colorTable.addElement(getForeground()); >+ colorTable.addElement(getBackground()); >+ setUnicode(); >+ } >+ /** >+ * Closes the RTF writer. Once closed no more content can be written. >+ * <b>NOTE:</b> <code>toString()</code> does not return a valid RTF string until >+ * <code>close()</code> has been called. >+ */ >+ public void close() { >+ if (isClosed() == false) { >+ writeHeader(); >+ write("\n}}\0"); >+ super.close(); >+ } >+ } >+ /** >+ * Returns the index of the specified color in the RTF color table. >+ * <p> >+ * >+ * @param color the color >+ * @param defaultIndex return value if color is null >+ * @return the index of the specified color in the RTF color table >+ * or "defaultIndex" if "color" is null. >+ */ >+ int getColorIndex(Color color, int defaultIndex) { >+ int index; >+ >+ if (color == null) { >+ index = defaultIndex; >+ } >+ else { >+ index = colorTable.indexOf(color); >+ if (index == -1) { >+ index = colorTable.size(); >+ colorTable.addElement(color); >+ } >+ } >+ return index; >+ } >+ /** >+ * Determines if Unicode RTF should be written. >+ * Don't write Unicode RTF on Windows 95/98/ME or NT. >+ */ >+ void setUnicode() { >+ final String Win95 = "windows 95"; >+ final String Win98 = "windows 98"; >+ final String WinME = "windows me"; >+ final String WinNT = "windows nt"; >+ String osName = System.getProperty("os.name").toLowerCase(); >+ String osVersion = System.getProperty("os.version"); >+ int majorVersion = 0; >+ >+ if (osName.startsWith(WinNT) && osVersion != null) { >+ int majorIndex = osVersion.indexOf('.'); >+ if (majorIndex != -1) { >+ osVersion = osVersion.substring(0, majorIndex); >+ try { >+ majorVersion = Integer.parseInt(osVersion); >+ } >+ catch (NumberFormatException exception) { >+ // ignore exception. version number remains unknown. >+ // will write without Unicode >+ } >+ } >+ } >+ if (osName != null && >+ osName.startsWith(Win95) == false && >+ osName.startsWith(Win98) == false && >+ osName.startsWith(WinME) == false && >+ (osName.startsWith(WinNT) == false || majorVersion > 4)) { >+ WriteUnicode = true; >+ } >+ else { >+ WriteUnicode = false; >+ } >+ } >+ /** >+ * Appends the specified segment of "string" to the RTF data. >+ * Copy from <code>start</code> up to, but excluding, <code>end</code>. >+ * <p> >+ * >+ * @param string string to copy a segment from. Must not contain >+ * line breaks. Line breaks should be written using writeLineDelimiter() >+ * @param start start offset of segment. 0 based. >+ * @param end end offset of segment >+ */ >+ void write(String string, int start, int end) { >+ for (int index = start; index < end; index++) { >+ char ch = string.charAt(index); >+ if (ch > 0xFF && WriteUnicode) { >+ // write the sub string from the last escaped character >+ // to the current one. Fixes bug 21698. >+ if (index > start) { >+ write(string.substring(start, index)); >+ } >+ write("\\u"); >+ write(Integer.toString((short) ch)); >+ write(' '); // control word delimiter >+ start = index + 1; >+ } >+ else >+ if (ch == '}' || ch == '{' || ch == '\\') { >+ // write the sub string from the last escaped character >+ // to the current one. Fixes bug 21698. >+ if (index > start) { >+ write(string.substring(start, index)); >+ } >+ write('\\'); >+ write(ch); >+ start = index + 1; >+ } >+ } >+ // write from the last escaped character to the end. >+ // Fixes bug 21698. >+ if (start < end) { >+ write(string.substring(start, end)); >+ } >+ } >+ /** >+ * Writes the RTF header including font table and color table. >+ */ >+ void writeHeader() { >+ StringBuffer header = new StringBuffer(); >+ FontData fontData = getFont().getFontData()[0]; >+ header.append("{\\rtf1\\ansi"); >+ // specify code page, necessary for copy to work in bidi >+ // systems that don't support Unicode RTF. >+ String cpg = System.getProperty("file.encoding").toLowerCase(); >+ if (cpg.startsWith("cp") || cpg.startsWith("ms")) { >+ cpg = cpg.substring(2, cpg.length()); >+ header.append("\\ansicpg"); >+ header.append(cpg); >+ } >+ header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil "); >+ header.append(fontData.getName()); >+ header.append(";}}\n{\\colortbl"); >+ for (int i = 0; i < colorTable.size(); i++) { >+ Color color = (Color) colorTable.elementAt(i); >+ header.append("\\red"); >+ header.append(color.getRed()); >+ header.append("\\green"); >+ header.append(color.getGreen()); >+ header.append("\\blue"); >+ header.append(color.getBlue()); >+ header.append(";"); >+ } >+ // some RTF readers ignore the deff0 font tag. Explicitly >+ // set the font for the whole document to work around this. >+ header.append("}\n{\\f0\\fs"); >+ // font size is specified in half points >+ header.append(fontData.getHeight() * 2); >+ header.append(" "); >+ write(header.toString(), 0); >+ } >+ /** >+ * Appends the specified line text to the RTF data. Lines will be formatted >+ * using the styles queried from the LineStyleListener, if set, or those set >+ * directly in the widget. >+ * <p> >+ * >+ * @param line line text to write as RTF. Must not contain line breaks >+ * Line breaks should be written using writeLineDelimiter() >+ * @param lineOffset offset of the line. 0 based from the start of the >+ * widget document. Any text occurring before the start offset or after the >+ * end offset specified during object creation is ignored. >+ * @exception SWTException <ul> >+ * <li>ERROR_IO when the writer is closed.</li> >+ * </ul> >+ */ >+ public void writeLine(String line, int lineOffset) { >+ StyleRange[] styles = new StyleRange[0]; >+ Color lineBackground = null; >+ StyledTextEvent event; >+ >+ if (isClosed()) { >+ SWT.error(SWT.ERROR_IO); >+ } >+ event = renderer.getLineStyleData(lineOffset, line); >+ if (event != null) { >+ styles = event.styles; >+ } >+ event = renderer.getLineBackgroundData(lineOffset, line); >+ if (event != null) { >+ lineBackground = event.lineBackground; >+ } >+ if (lineBackground == null) { >+ lineBackground = getBackground(); >+ } >+ writeStyledLine(line, lineOffset, styles, lineBackground); >+ } >+ /** >+ * Appends the specified line delmimiter to the RTF data. >+ * <p> >+ * >+ * @param lineDelimiter line delimiter to write as RTF. >+ * @exception SWTException <ul> >+ * <li>ERROR_IO when the writer is closed.</li> >+ * </ul> >+ */ >+ public void writeLineDelimiter(String lineDelimiter) { >+ if (isClosed()) { >+ SWT.error(SWT.ERROR_IO); >+ } >+ write(lineDelimiter, 0, lineDelimiter.length()); >+ write("\\par "); >+ } >+ /** >+ * Appends the specified line text to the RTF data. >+ * Use the colors and font styles specified in "styles" and "lineBackground". >+ * Formatting is written to reflect the text rendering by the text widget. >+ * Style background colors take precedence over the line background color. >+ * Background colors are written using the \highlight tag (vs. the \cb tag). >+ * <p> >+ * >+ * @param line line text to write as RTF. Must not contain line breaks >+ * Line breaks should be written using writeLineDelimiter() >+ * @param lineOffset offset of the line. 0 based from the start of the >+ * widget document. Any text occurring before the start offset or after the >+ * end offset specified during object creation is ignored. >+ * @param styles styles to use for formatting. Must not be null. >+ * @param linebackground line background color to use for formatting. >+ * May be null. >+ */ >+ void writeStyledLine(String line, int lineOffset, StyleRange[] styles, Color lineBackground) { >+ int lineLength = line.length(); >+ int lineIndex; >+ int copyEnd; >+ int startOffset = getStart(); >+ int endOffset = startOffset + super.getCharCount(); >+ int lineEndOffset = Math.min(lineLength, endOffset - lineOffset); >+ int writeOffset = startOffset - lineOffset; >+ >+ if (writeOffset >= line.length()) { >+ return; // whole line is outside write range >+ } >+ else >+ if (writeOffset > 0) { >+ lineIndex = writeOffset; // line starts before RTF write start >+ } >+ else { >+ lineIndex = 0; >+ } >+ if (lineBackground != null) { >+ write("{\\highlight"); >+ write(getColorIndex(lineBackground, DEFAULT_BACKGROUND)); >+ write(" "); >+ } >+ for (int i = 0; i < styles.length; i++) { >+ StyleRange style = styles[i]; >+ int start = style.start - lineOffset; >+ int end = start + style.length; >+ int colorIndex; >+ // skip over partial first line >+ if (end < writeOffset) { >+ continue; >+ } >+ // style starts beyond line end or RTF write end >+ if (start >= lineEndOffset) { >+ break; >+ } >+ // write any unstyled text >+ if (lineIndex < start) { >+ // copy to start of style >+ // style starting betond end of write range or end of line >+ // is guarded against above. >+ write(line, lineIndex, start); >+ lineIndex = start; >+ } >+ // write styled text >+ colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND); >+ write("{\\cf"); >+ write(getColorIndex(style.foreground, DEFAULT_FOREGROUND)); >+ if (colorIndex != DEFAULT_BACKGROUND) { >+ write("\\highlight"); >+ write(colorIndex); >+ } >+ if (style.fontStyle == SWT.BOLD) { >+ write("\\b"); >+ } >+ write(" "); >+ // copy to end of style or end of write range or end of line >+ copyEnd = Math.min(end, lineEndOffset); >+ // guard against invalid styles and let style processing continue >+ copyEnd = Math.max(copyEnd, lineIndex); >+ write(line, lineIndex, copyEnd); >+ if (style.fontStyle == SWT.BOLD) { >+ write("\\b0"); >+ } >+ write("}"); >+ lineIndex = copyEnd; >+ } >+ // write unstyled text at the end of the line >+ if (lineIndex < lineEndOffset) { >+ write(line, lineIndex, lineEndOffset); >+ } >+ if (lineBackground != null) { >+ write("}"); >+ } >+ } >+ } >+ /** >+ * The <code>TextWriter</code> class is used to write widget content to >+ * a string. Whole and partial lines and line breaks can be written. To write >+ * partial lines, specify the start and length of the desired segment >+ * during object creation. >+ * <p> >+ * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close() >+ * has been called. >+ */ >+ class TextWriter { >+ private StringBuffer buffer; >+ private int startOffset; // offset of first character that will be written >+ private int endOffset; // offset of last character that will be written. >+ // 0 based from the beginning of the widget text. >+ private boolean isClosed = false; >+ >+ /** >+ * Creates a writer that writes content starting at offset "start" >+ * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines. >+ * <p> >+ * >+ * @param start start offset of content to write, 0 based from beginning of document >+ * @param length length of content to write >+ */ >+ public TextWriter(int start, int length) { >+ buffer = new StringBuffer(length); >+ startOffset = start; >+ endOffset = start + length; >+ } >+ /** >+ * Closes the writer. Once closed no more content can be written. >+ * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless >+ * the writer is closed. >+ */ >+ public void close() { >+ if (isClosed == false) { >+ isClosed = true; >+ } >+ } >+ /** >+ * Returns the number of characters to write. >+ */ >+ public int getCharCount() { >+ return endOffset - startOffset; >+ } >+ /** >+ * Returns the offset where writing starts. 0 based from the start of >+ * the widget text. Used to write partial lines. >+ */ >+ public int getStart() { >+ return startOffset; >+ } >+ /** >+ * Returns whether the writer is closed. >+ */ >+ public boolean isClosed() { >+ return isClosed; >+ } >+ /** >+ * Returns the string. <code>close()</code> must be called before <code>toString()</code> >+ * is guaranteed to return a valid string. >+ * <p> >+ * >+ * @return the string >+ */ >+ public String toString() { >+ return buffer.toString(); >+ } >+ /** >+ * Appends the given string to the data. >+ */ >+ void write(String string) { >+ buffer.append(string); >+ } >+ /** >+ * Inserts the given string to the data at the specified offset. >+ * Do nothing if "offset" is < 0 or > getCharCount() >+ * <p> >+ * >+ * @param string text to insert >+ * @param offset offset in the existing data to insert "string" at. >+ */ >+ void write(String string, int offset) { >+ if (offset < 0 || offset > buffer.length()) { >+ return; >+ } >+ buffer.insert(offset, string); >+ } >+ /** >+ * Appends the given int to the data. >+ */ >+ void write(int i) { >+ buffer.append(i); >+ } >+ /** >+ * Appends the given character to the data. >+ */ >+ void write(char i) { >+ buffer.append(i); >+ } >+ /** >+ * Appends the specified line text to the data. >+ * <p> >+ * >+ * @param line line text to write. Must not contain line breaks >+ * Line breaks should be written using writeLineDelimiter() >+ * @param lineOffset offset of the line. 0 based from the start of the >+ * widget document. Any text occurring before the start offset or after the >+ * end offset specified during object creation is ignored. >+ * @exception SWTException <ul> >+ * <li>ERROR_IO when the writer is closed.</li> >+ * </ul> >+ */ >+ public void writeLine(String line, int lineOffset) { >+ int lineLength = line.length(); >+ int lineIndex; >+ int copyEnd; >+ int writeOffset = startOffset - lineOffset; >+ >+ if (isClosed) { >+ SWT.error(SWT.ERROR_IO); >+ } >+ if (writeOffset >= lineLength) { >+ return; // whole line is outside write range >+ } >+ else >+ if (writeOffset > 0) { >+ lineIndex = writeOffset; // line starts before write start >+ } >+ else { >+ lineIndex = 0; >+ } >+ copyEnd = Math.min(lineLength, endOffset - lineOffset); >+ if (lineIndex < copyEnd) { >+ write(line.substring(lineIndex, copyEnd)); >+ } >+ } >+ /** >+ * Appends the specified line delmimiter to the data. >+ * <p> >+ * >+ * @param lineDelimiter line delimiter to write >+ * @exception SWTException <ul> >+ * <li>ERROR_IO when the writer is closed.</li> >+ * </ul> >+ */ >+ public void writeLineDelimiter(String lineDelimiter) { >+ if (isClosed) { >+ SWT.error(SWT.ERROR_IO); >+ } >+ write(lineDelimiter); >+ } >+ } >+ /** >+ * LineCache provides an interface to calculate and invalidate >+ * line based data. >+ * Implementors need to return a line width in <code>getWidth</code>. >+ */ >+ interface LineCache { >+ /** >+ * Calculates the lines in the specified range. >+ * <p> >+ * >+ * @param startLine first line to calculate >+ * @param lineCount number of lines to calculate >+ */ >+ public void calculate(int startLine, int lineCount); >+ /** >+ * Returns a width that will be used by the <code>StyledText</code> >+ * widget to size a horizontal scroll bar. >+ * <p> >+ * >+ * @return the line width >+ */ >+ public int getWidth(); >+ /** >+ * Resets the lines in the specified range. >+ * This method is called in <code>StyledText.redraw()</code> >+ * and allows implementors to call redraw themselves during reset. >+ * <p> >+ * >+ * @param startLine the first line to reset >+ * @param lineCount the number of lines to reset >+ * @param calculateMaxWidth true=implementors should retain a >+ * valid width even if it is affected by the reset operation. >+ * false=the width may be set to 0 >+ */ >+ public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth); >+ /** >+ * Resets the lines in the specified range. >+ * <p> >+ * >+ * @param startLine the first line to reset >+ * @param lineCount the number of lines to reset >+ * @param calculateMaxWidth true=implementors should retain a >+ * valid width even if it is affected by the reset operation. >+ * false=the width may be set to 0 >+ */ >+ public void reset(int startLine, int lineCount, boolean calculateMaxWidth); >+ /** >+ * Called when a text change occurred. >+ * <p> >+ * >+ * @param startOffset the start offset of the text change >+ * @param newLineCount the number of inserted lines >+ * @param replaceLineCount the number of deleted lines >+ * @param newCharCount the number of new characters >+ * @param replaceCharCount the number of deleted characters >+ */ >+ public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount); >+ } >+ /** >+ * Keeps track of line widths and the longest line in the >+ * StyledText document. >+ * Line widths are calculated when requested by a call to >+ * <code>calculate</code> and cached until reset by a call >+ * to <code>redrawReset</code> or <code>reset</code>. >+ */ >+ class ContentWidthCache implements LineCache { >+ StyledText parent; // parent widget, used to create a GC for line measuring >+ int[] lineWidth; // width in pixel of each line in the document, -1 for unknown width >+ int lineCount; // number of lines in lineWidth array >+ int maxWidth; // maximum line width of all measured lines >+ int maxWidthLineIndex; // index of the widest line >+ >+ /** >+ * Creates a new <code>ContentWidthCache</code> and allocates space >+ * for the given number of lines. >+ * <p> >+ * >+ * @param parent the StyledText widget used to create a GC for >+ * line measuring >+ * @param lineCount initial number of lines to allocate space for >+ */ >+ public ContentWidthCache(StyledText parent, int lineCount) { >+ this.lineCount = lineCount; >+ this.parent = parent; >+ lineWidth = new int[lineCount]; >+ reset(0, lineCount, false); >+ } >+ /** >+ * Calculates the width of each line in the given range if it has >+ * not been calculated yet. >+ * If any line in the given range is wider than the currently widest >+ * line, the maximum line width is updated, >+ * <p> >+ * >+ * @param startLine first line to calculate the line width of >+ * @param lineCount number of lines to calculate the line width for >+ */ >+ public void calculate(int startLine, int lineCount) { >+ GC gc = null; >+ int caretWidth = 0; >+ int stopLine = startLine + lineCount; >+ >+ for (int i = startLine; i < stopLine; i++) { >+ if (lineWidth[i] == -1) { >+ String line = content.getLine(i); >+ int lineOffset = content.getOffsetAtLine(i); >+ >+ if (gc == null) { >+ gc = parent.getGC(); >+ caretWidth = getCaretWidth(); >+ } >+ lineWidth[i] = contentWidth(line, lineOffset, gc) + caretWidth; >+ } >+ if (lineWidth[i] > maxWidth) { >+ maxWidth = lineWidth[i]; >+ maxWidthLineIndex = i; >+ } >+ } >+ if (gc != null) { >+ gc.dispose(); >+ } >+ } >+ /** >+ * Calculates the width of the visible lines in the specified >+ * range. >+ * <p> >+ * >+ * @param startLine the first changed line >+ * @param newLineCount the number of inserted lines >+ */ >+ void calculateVisible(int startLine, int newLineCount) { >+ int topIndex = parent.getTopIndex(); >+ int bottomLine = Math.min(getPartialBottomIndex(), startLine + newLineCount); >+ >+ startLine = Math.max(startLine, topIndex); >+ calculate(startLine, bottomLine - startLine + 1); >+ } >+ /** >+ * Measures the width of the given line. >+ * <p> >+ * >+ * @param line the line to measure >+ * @param lineOffset start offset of the line to measure, relative >+ * to the start of the document >+ * @param gc the GC to use for measuring the line >+ * @param currentFont the font currently set in gc. Cached for better >+ * performance. Null when running in a bidi locale. >+ * @return the width of the given line >+ */ >+ int contentWidth(String line, int lineOffset, GC gc) { >+ int width; >+ >+ if (isBidi()) { >+ StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc); >+ width = bidi.getTextWidth(); >+ } >+ else { >+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >+ StyleRange[] styles = null; >+ if (event != null) { >+ styles = renderer.filterLineStyles(event.styles); >+ } >+ width = renderer.getTextWidth(line, lineOffset, 0, line.length(), styles, 0, gc); >+ } >+ return width + leftMargin; >+ } >+ /** >+ * Grows the <code>lineWidth</code> array to accomodate new line width >+ * information. >+ * <p> >+ * >+ * @param numLines the number of elements to increase the array by >+ */ >+ void expandLines(int numLines) { >+ int size = lineWidth.length; >+ if (size - lineCount >= numLines) { >+ return; >+ } >+ int[] newLines = new int[Math.max(size * 2, size + numLines)]; >+ System.arraycopy(lineWidth, 0, newLines, 0, size); >+ lineWidth = newLines; >+ reset(size, lineWidth.length - size, false); >+ } >+ /** >+ * Returns the width of the longest measured line. >+ * <p> >+ * >+ * @return the width of the longest measured line. >+ */ >+ public int getWidth() { >+ return maxWidth; >+ } >+ /** >+ * Updates the line width array to reflect inserted or deleted lines. >+ * <p> >+ * >+ * @param start the starting line of the change that took place >+ * @param delta the number of lines in the change, > 0 indicates lines inserted, >+ * < 0 indicates lines deleted >+ */ >+ void linesChanged(int startLine, int delta) { >+ boolean inserting = delta > 0; >+ >+ if (delta == 0) { >+ return; >+ } >+ if (inserting) { >+ // shift the lines down to make room for new lines >+ expandLines(delta); >+ for (int i = lineCount - 1; i >= startLine; i--) { >+ lineWidth[i + delta] = lineWidth[i]; >+ } >+ // reset the new lines >+ for (int i = startLine + 1; i <= startLine + delta && i < lineWidth.length; i++) { >+ lineWidth[i] = -1; >+ } >+ // have new lines been inserted above the longest line? >+ if (maxWidthLineIndex >= startLine) { >+ maxWidthLineIndex += delta; >+ } >+ } >+ else { >+ // shift up the lines >+ for (int i = startLine - delta; i < lineCount; i++) { >+ lineWidth[i+delta] = lineWidth[i]; >+ } >+ // has the longest line been removed? >+ if (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine - delta) { >+ maxWidth = 0; >+ maxWidthLineIndex = -1; >+ } >+ else >+ if (maxWidthLineIndex >= startLine - delta) { >+ maxWidthLineIndex += delta; >+ } >+ } >+ lineCount += delta; >+ } >+ /** >+ * Resets the line width of the lines in the specified range. >+ * <p> >+ * >+ * @param startLine the first line to reset >+ * @param lineCount the number of lines to reset >+ * @param calculateMaxWidth true=if the widest line is being >+ * reset the maximum width of all remaining cached lines is >+ * calculated. false=the maximum width is set to 0 if the >+ * widest line is being reset. >+ */ >+ public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) { >+ reset(startLine, lineCount, calculateMaxWidth); >+ } >+ /** >+ * Resets the line width of the lines in the specified range. >+ * <p> >+ * >+ * @param startLine the first line to reset >+ * @param lineCount the number of lines to reset >+ * @param calculateMaxWidth true=if the widest line is being >+ * reset the maximum width of all remaining cached lines is >+ * calculated. false=the maximum width is set to 0 if the >+ * widest line is being reset. >+ */ >+ public void reset(int startLine, int lineCount, boolean calculateMaxWidth) { >+ int endLine = startLine + lineCount; >+ >+ if (startLine < 0 || endLine > lineWidth.length) { >+ return; >+ } >+ for (int i = startLine; i < endLine; i++) { >+ lineWidth[i] = -1; >+ } >+ // if the longest line is one of the reset lines, the maximum line >+ // width is no longer valid >+ if (maxWidthLineIndex >= startLine && maxWidthLineIndex < endLine) { >+ maxWidth = 0; >+ maxWidthLineIndex = -1; >+ if (calculateMaxWidth) { >+ for (int i = 0; i < lineCount; i++) { >+ if (lineWidth[i] > maxWidth) { >+ maxWidth = lineWidth[i]; >+ maxWidthLineIndex = i; >+ } >+ } >+ } >+ } >+ } >+ /** >+ * Updates the line width array to reflect a text change. >+ * Lines affected by the text change will be reset. >+ * <p> >+ * >+ * @param startOffset the start offset of the text change >+ * @param newLineCount the number of inserted lines >+ * @param replaceLineCount the number of deleted lines >+ * @param newCharCount the number of new characters >+ * @param replaceCharCount the number of deleted characters >+ */ >+ public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) { >+ int startLine = parent.getLineAtOffset(startOffset); >+ boolean removedMaxLine = (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine + replaceLineCount); >+ // entire text deleted? >+ if (startLine == 0 && replaceLineCount == lineCount) { >+ lineCount = newLineCount; >+ lineWidth = new int[lineCount]; >+ reset(0, lineCount, false); >+ maxWidth = 0; >+ } >+ else { >+ linesChanged(startLine, -replaceLineCount); >+ linesChanged(startLine, newLineCount); >+ lineWidth[startLine] = -1; >+ } >+ // only calculate the visible lines. otherwise measurements of changed lines >+ // outside the visible area may subsequently change again without the >+ // lines ever being visible. >+ calculateVisible(startLine, newLineCount); >+ // maxWidthLineIndex will be -1 (i.e., unknown line width) if the widget has >+ // not been visible yet and the changed lines have therefore not been >+ // calculated above. >+ if (removedMaxLine || >+ (maxWidthLineIndex != -1 && lineWidth[maxWidthLineIndex] < maxWidth)) { >+ // longest line has been removed or changed and is now shorter. >+ // need to recalculate maximum content width for all lines >+ maxWidth = 0; >+ for (int i = 0; i < lineCount; i++) { >+ if (lineWidth[i] > maxWidth) { >+ maxWidth = lineWidth[i]; >+ maxWidthLineIndex = i; >+ } >+ } >+ } >+ } >+ } >+ /** >+ * Updates the line wrapping of the content. >+ * The line wrapping must always be in a consistent state. >+ * Therefore, when <code>reset</code> or <code>redrawReset</code> >+ * is called, the line wrapping is recalculated immediately >+ * instead of in <code>calculate</code>. >+ */ >+ class WordWrapCache implements LineCache { >+ StyledText parent; >+ WrappedContent visualContent; >+ >+ /** >+ * Creates a new <code>WordWrapCache</code> and calculates an initial >+ * line wrapping. >+ * <p> >+ * >+ * @param parent the StyledText widget to wrap content in. >+ * @param content the content provider that does the actual line wrapping. >+ */ >+ public WordWrapCache(StyledText parent, WrappedContent content) { >+ this.parent = parent; >+ visualContent = content; >+ visualContent.wrapLines(); >+ } >+ /** >+ * Do nothing. Lines are wrapped immediately after reset. >+ * <p> >+ * >+ * @param startLine first line to calculate >+ * @param lineCount number of lines to calculate >+ */ >+ public void calculate(int startLine, int lineCount) { >+ } >+ /** >+ * Returns the client area width. Lines are wrapped so there >+ * is no horizontal scroll bar. >+ * <p> >+ * >+ * @return the line width >+ */ >+ public int getWidth() { >+ return parent.getClientArea().width; >+ } >+ /** >+ * Wraps the lines in the specified range. >+ * This method is called in <code>StyledText.redraw()</code>. >+ * A redraw is therefore not necessary. >+ * <p> >+ * >+ * @param startLine the first line to reset >+ * @param lineCount the number of lines to reset >+ * @param calculateMaxWidth true=implementors should retain a >+ * valid width even if it is affected by the reset operation. >+ * false=the width may be set to 0 >+ */ >+ public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) { >+ if (lineCount == visualContent.getLineCount()) { >+ // do a full rewrap if all lines are reset >+ visualContent.wrapLines(); >+ } >+ else { >+ visualContent.reset(startLine, lineCount); >+ } >+ } >+ /** >+ * Rewraps the lines in the specified range and redraws >+ * the widget if the line wrapping has changed. >+ * <p> >+ * >+ * @param startLine the first line to reset >+ * @param lineCount the number of lines to reset >+ * @param calculateMaxWidth true=implementors should retain a >+ * valid width even if it is affected by the reset operation. >+ * false=the width may be set to 0 >+ */ >+ public void reset(int startLine, int lineCount, boolean calculateMaxWidth) { >+ int itemCount = getPartialBottomIndex() - topIndex + 1; >+ int[] oldLineOffsets = new int[itemCount]; >+ >+ for (int i = 0; i < itemCount; i++) { >+ oldLineOffsets[i] = visualContent.getOffsetAtLine(i + topIndex); >+ } >+ redrawReset(startLine, lineCount, calculateMaxWidth); >+ // check for cases which will require a full redraw >+ if (getPartialBottomIndex() - topIndex + 1 != itemCount) { >+ // number of visible lines has changed >+ parent.internalRedraw(); >+ } >+ else { >+ for (int i = 0; i < itemCount; i++) { >+ if (visualContent.getOffsetAtLine(i + topIndex) != oldLineOffsets[i]) { >+ // wrapping of one of the visible lines has changed >+ parent.internalRedraw(); >+ break; >+ } >+ } >+ } >+ } >+ /** >+ * Passes the text change notification to the line wrap content. >+ * <p> >+ * >+ * @param startOffset the start offset of the text change >+ * @param newLineCount the number of inserted lines >+ * @param replaceLineCount the number of deleted lines >+ * @param newCharCount the number of new characters >+ * @param replaceCharCount the number of deleted characters >+ */ >+ public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) { >+ int startLine = visualContent.getLineAtOffset(startOffset); >+ >+ visualContent.textChanged(startOffset, newLineCount, replaceLineCount, newCharCount, replaceCharCount); >+ if (startLine <= getPartialBottomIndex()) { >+ // only redraw if the text change is inside or above the >+ // visible lines. if it is below the visible lines it will >+ // not affect the word wrapping. fixes bug 14047. >+ parent.internalRedraw(); >+ } >+ } >+ } > > /** > * Constructs a new instance of this class given its parent >@@ -1552,7 +1558,7 @@ > * <p> > * The style value is either one of the style constants defined in > * class <code>SWT</code> which is applicable to instances of this >- * class, or must be built by <em>bitwise OR</em>'ing together >+ * class, or must be built by <em>bitwise OR</em>'ing together > * (that is, using the <code>int</code> "|" operator) two or more > * of those <code>SWT</code> style constants. The class description > * lists the style constants that are applicable to the class. >@@ -1577,60 +1583,64 @@ > * @see #getStyle > */ > public StyledText(Composite parent, int style) { >- super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND)); >- // set the bg/fg in the OS to ensure that these are the same as StyledText, necessary >- // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses >- super.setForeground(getForeground()); >- super.setBackground(getBackground()); >- Display display = getDisplay(); >- isBidi = StyledTextBidi.isBidiPlatform(); >- if ((style & SWT.READ_ONLY) != 0) { >- setEditable(false); >- } >- if ((style & SWT.BORDER) == 0 || (style & SWT.SINGLE) == 0) { >- leftMargin = topMargin = rightMargin = bottomMargin = 0; >- } >- clipboard = new Clipboard(display); >- installDefaultContent(); >- initializeRenderer(); >- if ((style & SWT.WRAP) != 0) { >- setWordWrap(true); >- } >- else { >- lineCache = new ContentWidthCache(this, content.getLineCount()); >- } >- if (isBidi() == false) { >- Caret caret = new Caret(this, SWT.NULL); >- caret.setSize(1, caret.getSize().y); >- } >- else { >- createCaretBitmaps(); >- new Caret(this, SWT.NULL); >- setBidiCaretDirection(); >- Runnable runnable = new Runnable() { >- public void run() { >- // setBidiCaretLocation calculates caret location like during >- // cursor movement and takes keyboard language into account. >- // Fixes 1GKPYMK >- setBidiCaretLocation(null); >- } >- }; >- StyledTextBidi.addLanguageListener(this, runnable); >- } >- >- String platform= SWT.getPlatform(); >- isCarbon = "carbon".equals(platform); >- >- // set the caret width, the height of the caret will default to the line height >- calculateScrollBars(); >- createKeyBindings(); >- ibeamCursor = new Cursor(display, SWT.CURSOR_IBEAM); >- setCursor(ibeamCursor); >- installListeners(); >- installDefaultLineStyler(); >+ super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND)); >+ // set the bg/fg in the OS to ensure that these are the same as StyledText, necessary >+ // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses >+ super.setForeground(getForeground()); >+ super.setBackground(getBackground()); >+ Display display = getDisplay(); >+ isBidi = StyledTextBidi.isBidiPlatform(); >+ if ((style & SWT.READ_ONLY) != 0) { >+ setEditable(false); >+ } >+ if ((style & SWT.BORDER) == 0 || (style & SWT.SINGLE) == 0) { >+ leftMargin = topMargin = rightMargin = bottomMargin = 0; >+ } >+ clipboard = new Clipboard(display); >+ installDefaultContent(); >+ initializeRenderer(); >+ if ((style & SWT.WRAP) != 0) { >+ setWordWrap(true); >+ } >+ else { >+ lineCache = new ContentWidthCache(this, content.getLineCount()); >+ } >+ if (isBidi() == false) { >+ Caret caret = new Caret(this, SWT.NULL); >+ caret.setSize(1, caret.getSize().y); >+ } >+ else { >+ createCaretBitmaps(); >+ new Caret(this, SWT.NULL); >+ setBidiCaretDirection(); >+ Runnable runnable = new Runnable() { >+ public void run() { >+ // setBidiCaretLocation calculates caret location like during >+ // cursor movement and takes keyboard language into account. >+ // Fixes 1GKPYMK >+ setBidiCaretLocation(null); >+ } >+ }; >+ StyledTextBidi.addLanguageListener(this, runnable); >+ } >+ >+ String platform= SWT.getPlatform(); >+ isCarbon = "carbon".equals(platform); >+ >+ // set the caret width, the height of the caret will default to the line height >+ calculateScrollBars(); >+ createKeyBindings(); >+ >+ ibeamCursor = new Cursor(display, SWT.CURSOR_IBEAM); >+ arrowCursor = new Cursor(display, SWT.CURSOR_ARROW); >+ currentCursor = ibeamCursor; >+ super.setCursor(currentCursor); >+ >+ installListeners(); >+ installDefaultLineStyler(); > } >-/** >- * Adds an extended modify listener. An ExtendedModify event is sent by the >+/** >+ * Adds an extended modify listener. An ExtendedModify event is sent by the > * widget when the widget text has changed. > * <p> > * >@@ -1644,75 +1654,75 @@ > * </ul> > */ > public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) { >- checkWidget(); >- if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >- StyledTextListener typedListener = new StyledTextListener(extendedModifyListener); >- addListener(ExtendedModify, typedListener); >+ checkWidget(); >+ if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ StyledTextListener typedListener = new StyledTextListener(extendedModifyListener); >+ addListener(ExtendedModify, typedListener); > } >-/** >+/** > * Maps a key to an action. >- * One action can be associated with N keys. However, each key can only >+ * One action can be associated with N keys. However, each key can only > * have one action (key:action is N:1 relation). > * <p> > * >- * @param key a key code defined in SWT.java or a character. >- * Optionally ORd with a state mask. Preferred state masks are one or more of >- * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform >+ * @param key a key code defined in SWT.java or a character. >+ * Optionally ORd with a state mask. Preferred state masks are one or more of >+ * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform > * differences. However, there may be cases where using the specific state masks > * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense. >- * @param action one of the predefined actions defined in ST.java. >- * Use SWT.NULL to remove a key binding. >+ * @param action one of the predefined actions defined in ST.java. >+ * Use SWT.NULL to remove a key binding. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > public void setKeyBinding(int key, int action) { >- checkWidget(); >- >- int keyValue = key & SWT.KEY_MASK; >- int modifierValue = key & SWT.MODIFIER_MASK; >- char keyChar = (char)keyValue; >- >- if (Character.isLetter(keyChar)) { >- // make the keybinding case insensitive by adding it >- // in its upper and lower case form >- char ch = Character.toUpperCase(keyChar); >- int newKey = ch | modifierValue; >- if (action == SWT.NULL) { >- keyActionMap.remove(new Integer(newKey)); >- } >- else { >- keyActionMap.put(new Integer(newKey), new Integer(action)); >- } >- ch = Character.toLowerCase(keyChar); >- newKey = ch | modifierValue; >- if (action == SWT.NULL) { >- keyActionMap.remove(new Integer(newKey)); >- } >- else { >- keyActionMap.put(new Integer(newKey), new Integer(action)); >- } >- } else { >- if (action == SWT.NULL) { >- keyActionMap.remove(new Integer(key)); >- } >- else { >- keyActionMap.put(new Integer(key), new Integer(action)); >- } >- } >- >-} >-/** >- * Adds a bidirectional segment listener. A BidiSegmentEvent is sent >- * whenever a line of text is measured or rendered. The user can >- * specify text ranges in the line that should be treated as if they >+ checkWidget(); >+ >+ int keyValue = key & SWT.KEY_MASK; >+ int modifierValue = key & SWT.MODIFIER_MASK; >+ char keyChar = (char)keyValue; >+ >+ if (Character.isLetter(keyChar)) { >+ // make the keybinding case insensitive by adding it >+ // in its upper and lower case form >+ char ch = Character.toUpperCase(keyChar); >+ int newKey = ch | modifierValue; >+ if (action == SWT.NULL) { >+ keyActionMap.remove(new Integer(newKey)); >+ } >+ else { >+ keyActionMap.put(new Integer(newKey), new Integer(action)); >+ } >+ ch = Character.toLowerCase(keyChar); >+ newKey = ch | modifierValue; >+ if (action == SWT.NULL) { >+ keyActionMap.remove(new Integer(newKey)); >+ } >+ else { >+ keyActionMap.put(new Integer(newKey), new Integer(action)); >+ } >+ } else { >+ if (action == SWT.NULL) { >+ keyActionMap.remove(new Integer(key)); >+ } >+ else { >+ keyActionMap.put(new Integer(key), new Integer(action)); >+ } >+ } >+ >+} >+/** >+ * Adds a bidirectional segment listener. A BidiSegmentEvent is sent >+ * whenever a line of text is measured or rendered. The user can >+ * specify text ranges in the line that should be treated as if they > * had a different direction than the surrounding text. > * This may be used when adjacent segments of right-to-left text should >- * not be reordered relative to each other. >+ * not be reordered relative to each other. > * E.g., Multiple Java string literals in a right-to-left language > * should generally remain in logical order to each other, that is, the >- * way they are stored. >+ * way they are stored. > * <p> > * > * @param listener the listener >@@ -1727,15 +1737,15 @@ > * @since 2.0 > */ > public void addBidiSegmentListener(BidiSegmentListener listener) { >- checkWidget(); >- if (listener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- StyledTextListener typedListener = new StyledTextListener(listener); >- addListener(LineGetSegments, typedListener); >+ checkWidget(); >+ if (listener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ StyledTextListener typedListener = new StyledTextListener(listener); >+ addListener(LineGetSegments, typedListener); > } > /** >- * Adds a line background listener. A LineGetBackground event is sent by the >+ * Adds a line background listener. A LineGetBackground event is sent by the > * widget to determine the background color for a line. > * <p> > * >@@ -1749,18 +1759,18 @@ > * </ul> > */ > public void addLineBackgroundListener(LineBackgroundListener listener) { >- checkWidget(); >- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >- if (userLineBackground == false) { >- removeLineBackgroundListener(defaultLineStyler); >- defaultLineStyler.setLineBackground(0, logicalContent.getLineCount(), null); >- userLineBackground = true; >- } >- StyledTextListener typedListener = new StyledTextListener(listener); >- addListener(LineGetBackground, typedListener); >+ checkWidget(); >+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ if (userLineBackground == false) { >+ removeLineBackgroundListener(defaultLineStyler); >+ defaultLineStyler.setLineBackground(0, logicalContent.getLineCount(), null); >+ userLineBackground = true; >+ } >+ StyledTextListener typedListener = new StyledTextListener(listener); >+ addListener(LineGetBackground, typedListener); > } > /** >- * Adds a line style listener. A LineGetStyle event is sent by the widget to >+ * Adds a line style listener. A LineGetStyle event is sent by the widget to > * determine the styles for a line. > * <p> > * >@@ -1774,20 +1784,20 @@ > * </ul> > */ > public void addLineStyleListener(LineStyleListener listener) { >- checkWidget(); >- if (listener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- if (userLineStyle == false) { >- removeLineStyleListener(defaultLineStyler); >- defaultLineStyler.setStyleRange(null); >- userLineStyle = true; >- } >- StyledTextListener typedListener = new StyledTextListener(listener); >- addListener(LineGetStyle, typedListener); >+ checkWidget(); >+ if (listener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ if (userLineStyle == false) { >+ removeLineStyleListener(defaultLineStyler); >+ defaultLineStyler.setStyleRange(null); >+ userLineStyle = true; >+ } >+ StyledTextListener typedListener = new StyledTextListener(listener); >+ addListener(LineGetStyle, typedListener); > } >-/** >- * Adds a modify listener. A Modify event is sent by the widget when the widget text >+/** >+ * Adds a modify listener. A Modify event is sent by the widget when the widget text > * has changed. > * <p> > * >@@ -1801,15 +1811,15 @@ > * </ul> > */ > public void addModifyListener(ModifyListener modifyListener) { >- checkWidget(); >- if (modifyListener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- TypedListener typedListener = new TypedListener(modifyListener); >- addListener(SWT.Modify, typedListener); >+ checkWidget(); >+ if (modifyListener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ TypedListener typedListener = new TypedListener(modifyListener); >+ addListener(SWT.Modify, typedListener); > } >-/** >- * Adds a selection listener. A Selection event is sent by the widget when the >+/** >+ * Adds a selection listener. A Selection event is sent by the widget when the > * selection has changed. > * <p> > * >@@ -1823,17 +1833,17 @@ > * </ul> > */ > public void addSelectionListener(SelectionListener listener) { >- checkWidget(); >- if (listener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- TypedListener typedListener = new TypedListener(listener); >- addListener(SWT.Selection, typedListener); >-} >-/** >- * Adds a verify key listener. A VerifyKey event is sent by the widget when a key >- * is pressed. The widget ignores the key press if the listener sets the doit field >- * of the event to false. >+ checkWidget(); >+ if (listener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ TypedListener typedListener = new TypedListener(listener); >+ addListener(SWT.Selection, typedListener); >+} >+/** >+ * Adds a verify key listener. A VerifyKey event is sent by the widget when a key >+ * is pressed. The widget ignores the key press if the listener sets the doit field >+ * of the event to false. > * <p> > * > * @param listener the listener >@@ -1846,17 +1856,17 @@ > * </ul> > */ > public void addVerifyKeyListener(VerifyKeyListener listener) { >- checkWidget(); >- if (listener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- StyledTextListener typedListener = new StyledTextListener(listener); >- addListener(VerifyKey, typedListener); >-} >-/** >- * Adds a verify listener. A Verify event is sent by the widget when the widget text >- * is about to change. The listener can set the event text and the doit field to >- * change the text that is set in the widget or to force the widget to ignore the >+ checkWidget(); >+ if (listener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ StyledTextListener typedListener = new StyledTextListener(listener); >+ addListener(VerifyKey, typedListener); >+} >+/** >+ * Adds a verify listener. A Verify event is sent by the widget when the widget text >+ * is about to change. The listener can set the event text and the doit field to >+ * change the text that is set in the widget or to force the widget to ignore the > * text change. > * <p> > * >@@ -1870,14 +1880,14 @@ > * </ul> > */ > public void addVerifyListener(VerifyListener verifyListener) { >- checkWidget(); >- if (verifyListener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- TypedListener typedListener = new TypedListener(verifyListener); >- addListener(SWT.Verify, typedListener); >+ checkWidget(); >+ if (verifyListener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ TypedListener typedListener = new TypedListener(verifyListener); >+ addListener(SWT.Verify, typedListener); > } >-/** >+/** > * Appends a string to the text at the end of the widget. > * <p> > * >@@ -1892,36 +1902,36 @@ > * </ul> > */ > public void append(String string) { >- checkWidget(); >- if (string == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- int lastChar = Math.max(getCharCount(), 0); >- replaceTextRange(lastChar, 0, string); >+ checkWidget(); >+ if (string == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ int lastChar = Math.max(getCharCount(), 0); >+ replaceTextRange(lastChar, 0, string); > } > /** > * Calculates the width of the widest visible line. > */ > void calculateContentWidth() { >- if (lineHeight != 0) { >- lineCache = getLineCache(content); >- lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1); >- } >+ if (lineHeight != 0) { >+ lineCache = getLineCache(content); >+ lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1); >+ } > } > /** > * Calculates the scroll bars > */ > void calculateScrollBars() { >- ScrollBar horizontalBar = getHorizontalBar(); >- ScrollBar verticalBar = getVerticalBar(); >- >- setScrollBars(); >- if (verticalBar != null) { >- verticalBar.setIncrement(getVerticalIncrement()); >- } >- if (horizontalBar != null) { >- horizontalBar.setIncrement(getHorizontalIncrement()); >- } >+ ScrollBar horizontalBar = getHorizontalBar(); >+ ScrollBar verticalBar = getVerticalBar(); >+ >+ setScrollBars(); >+ if (verticalBar != null) { >+ verticalBar.setIncrement(getVerticalIncrement()); >+ } >+ if (horizontalBar != null) { >+ horizontalBar.setIncrement(getHorizontalIncrement()); >+ } > } > /** > * Calculates the top index based on the current vertical scroll offset. >@@ -1930,97 +1940,97 @@ > * The top index starts at 0. > */ > void calculateTopIndex() { >- int oldTopIndex = topIndex; >- int verticalIncrement = getVerticalIncrement(); >- int clientAreaHeight = getClientArea().height; >- >- if (verticalIncrement == 0) { >- return; >- } >- topIndex = Compatibility.ceil(verticalScrollOffset, verticalIncrement); >- // Set top index to partially visible top line if no line is fully >- // visible but at least some of the widget client area is visible. >- // Fixes bug 15088. >- if (topIndex > 0) { >- if (clientAreaHeight > 0) { >- int bottomPixel = verticalScrollOffset + clientAreaHeight; >- int fullLineTopPixel = topIndex * verticalIncrement; >- int fullLineVisibleHeight = bottomPixel - fullLineTopPixel; >- // set top index to partially visible line if no line fully fits in >- // client area or if space is available but not used (the latter should >- // never happen because we use claimBottomFreeSpace) >- if (fullLineVisibleHeight < verticalIncrement) { >- topIndex--; >- } >- } >- else >- if (topIndex >= content.getLineCount()) { >- topIndex = content.getLineCount() - 1; >- } >- } >- if (topIndex != oldTopIndex) { >- topOffset = content.getOffsetAtLine(topIndex); >- lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1); >- setHorizontalScrollBar(); >- } >+ int oldTopIndex = topIndex; >+ int verticalIncrement = getVerticalIncrement(); >+ int clientAreaHeight = getClientArea().height; >+ >+ if (verticalIncrement == 0) { >+ return; >+ } >+ topIndex = Compatibility.ceil(verticalScrollOffset, verticalIncrement); >+ // Set top index to partially visible top line if no line is fully >+ // visible but at least some of the widget client area is visible. >+ // Fixes bug 15088. >+ if (topIndex > 0) { >+ if (clientAreaHeight > 0) { >+ int bottomPixel = verticalScrollOffset + clientAreaHeight; >+ int fullLineTopPixel = topIndex * verticalIncrement; >+ int fullLineVisibleHeight = bottomPixel - fullLineTopPixel; >+ // set top index to partially visible line if no line fully fits in >+ // client area or if space is available but not used (the latter should >+ // never happen because we use claimBottomFreeSpace) >+ if (fullLineVisibleHeight < verticalIncrement) { >+ topIndex--; >+ } >+ } >+ else >+ if (topIndex >= content.getLineCount()) { >+ topIndex = content.getLineCount() - 1; >+ } >+ } >+ if (topIndex != oldTopIndex) { >+ topOffset = content.getOffsetAtLine(topIndex); >+ lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1); >+ setHorizontalScrollBar(); >+ } > } > /** > * Hides the scroll bars if widget is created in single line mode. > */ > static int checkStyle(int style) { >- if ((style & SWT.SINGLE) != 0) { >- style &= ~(SWT.H_SCROLL | SWT.V_SCROLL); >- } >- return style; >+ if ((style & SWT.SINGLE) != 0) { >+ style &= ~(SWT.H_SCROLL | SWT.V_SCROLL); >+ } >+ return style; > } > /** >- * Scrolls down the text to use new space made available by a resize or by >+ * Scrolls down the text to use new space made available by a resize or by > * deleted lines. > */ > void claimBottomFreeSpace() { >- int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - getClientArea().height); >- >- if (newVerticalOffset < verticalScrollOffset) { >- // Scroll up so that empty lines below last text line are used. >- // Fixes 1GEYJM0 >- setVerticalScrollOffset(newVerticalOffset, true); >- } >+ int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - getClientArea().height); >+ >+ if (newVerticalOffset < verticalScrollOffset) { >+ // Scroll up so that empty lines below last text line are used. >+ // Fixes 1GEYJM0 >+ setVerticalScrollOffset(newVerticalOffset, true); >+ } > } > /** > * Scrolls text to the right to use new space made available by a resize. > */ > void claimRightFreeSpace() { >- int newHorizontalOffset = Math.max(0, lineCache.getWidth() - (getClientArea().width - leftMargin - rightMargin)); >- >- if (newHorizontalOffset < horizontalScrollOffset) { >- // item is no longer drawn past the right border of the client area >- // align the right end of the item with the right border of the >- // client area (window is scrolled right). >- scrollHorizontalBar(newHorizontalOffset - horizontalScrollOffset); >- } >+ int newHorizontalOffset = Math.max(0, lineCache.getWidth() - (getClientArea().width - leftMargin - rightMargin)); >+ >+ if (newHorizontalOffset < horizontalScrollOffset) { >+ // item is no longer drawn past the right border of the client area >+ // align the right end of the item with the right border of the >+ // client area (window is scrolled right). >+ scrollHorizontalBar(newHorizontalOffset - horizontalScrollOffset); >+ } > } > /** > * Clears the widget margin. >- * >+ * > * @param gc GC to render on > * @param background background color to use for clearing the margin > * @param clientArea widget client area dimensions > * @param renderHeight height in pixel of the rendered lines > */ > void clearMargin(GC gc, Color background, Rectangle clientArea, int renderHeight) { >- if (renderHeight + topMargin <= 0) { >- return; >- } >- // clear the margin background >- gc.setBackground(background); >- gc.fillRectangle(0, 0, clientArea.width, topMargin); >- gc.fillRectangle(0, 0, leftMargin, renderHeight + topMargin); >- gc.fillRectangle( >- 0, clientArea.height - bottomMargin, >- clientArea.width, bottomMargin); >- gc.fillRectangle( >- clientArea.width - rightMargin, 0, >- rightMargin, renderHeight + topMargin); >+ if (renderHeight + topMargin <= 0) { >+ return; >+ } >+ // clear the margin background >+ gc.setBackground(background); >+ gc.fillRectangle(0, 0, clientArea.width, topMargin); >+ gc.fillRectangle(0, 0, leftMargin, renderHeight + topMargin); >+ gc.fillRectangle( >+ 0, clientArea.height - bottomMargin, >+ clientArea.width, bottomMargin); >+ gc.fillRectangle( >+ clientArea.width - rightMargin, 0, >+ rightMargin, renderHeight + topMargin); > } > /** > * Removes the widget selection. >@@ -2029,24 +2039,24 @@ > * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset. > */ > void clearSelection(boolean sendEvent) { >- int selectionStart = selection.x; >- int selectionEnd = selection.y; >- int length = content.getCharCount(); >- >- resetSelection(); >- // redraw old selection, if any >- if (selectionEnd - selectionStart > 0) { >- // called internally to remove selection after text is removed >- // therefore make sure redraw range is valid. >- int redrawStart = Math.min(selectionStart, length); >- int redrawEnd = Math.min(selectionEnd, length); >- if (redrawEnd - redrawStart > 0) { >- internalRedrawRange(redrawStart, redrawEnd - redrawStart, true); >- } >- if (sendEvent == true) { >- sendSelectionEvent(); >- } >- } >+ int selectionStart = selection.x; >+ int selectionEnd = selection.y; >+ int length = content.getCharCount(); >+ >+ resetSelection(); >+ // redraw old selection, if any >+ if (selectionEnd - selectionStart > 0) { >+ // called internally to remove selection after text is removed >+ // therefore make sure redraw range is valid. >+ int redrawStart = Math.min(selectionStart, length); >+ int redrawEnd = Math.min(selectionEnd, length); >+ if (redrawEnd - redrawStart > 0) { >+ internalRedrawRange(redrawStart, redrawEnd - redrawStart, true); >+ } >+ if (sendEvent == true) { >+ sendSelectionEvent(); >+ } >+ } > } > /** > * Computes the preferred size. >@@ -2057,77 +2067,77 @@ > * </ul> > */ > public Point computeSize (int wHint, int hHint, boolean changed) { >- checkWidget(); >- int count, width, height; >- boolean singleLine = (getStyle() & SWT.SINGLE) != 0; >- >- if (singleLine) { >- count = 1; >- } else { >- count = content.getLineCount(); >- } >- if (wHint != SWT.DEFAULT) { >- width = wHint; >- } >- else { >- width = DEFAULT_WIDTH; >- } >- >- if (wordWrap) { >- if (((WrappedContent) content).getVisualLineCount() != 0) { >- // lines have already been wrapped to a specific width. >- // use existing line count. fixes bug 9191 >- if (wHint == SWT.DEFAULT) { >- width = lineCache.getWidth(); >- } else { >- ((WrappedContent) content).wrapLines(width); >- // caret may be on a different line after a rewrap >- setCaretLocation(); >- } >- if (singleLine == false) { >- count = content.getLineCount(); >- } >- } >- else { >- if (singleLine == false) { >- ((WrappedContent) content).wrapLines(width); >- // caret may be on a different line after a rewrap >- setCaretLocation(); >- count = content.getLineCount(); >- } >- } >- } >- else if (wHint == SWT.DEFAULT) { >- // Only calculate what can actually be displayed. >- // Do this because measuring each text line is a >- // time-consuming process. >- int visibleCount = Math.min (count, getDisplay().getBounds().height / lineHeight); >- lineCache.calculate(0, visibleCount); >- width = lineCache.getWidth() + leftMargin + rightMargin; >- } >- if (hHint != SWT.DEFAULT) { >- height = hHint; >- } >- else { >- height = count * lineHeight + topMargin + bottomMargin; >- } >- // Use default values if no text is defined. >- if (width == 0) { >- width = DEFAULT_WIDTH; >- } >- if (height == 0) { >- if (singleLine) { >- height = lineHeight; >- } >- else { >- height = DEFAULT_HEIGHT; >- } >- } >- Rectangle rect = computeTrim(0, 0, width, height); >- return new Point (rect.width, rect.height); >+ checkWidget(); >+ int count, width, height; >+ boolean singleLine = (getStyle() & SWT.SINGLE) != 0; >+ >+ if (singleLine) { >+ count = 1; >+ } else { >+ count = content.getLineCount(); >+ } >+ if (wHint != SWT.DEFAULT) { >+ width = wHint; >+ } >+ else { >+ width = DEFAULT_WIDTH; >+ } >+ >+ if (wordWrap) { >+ if (((WrappedContent) content).getVisualLineCount() != 0) { >+ // lines have already been wrapped to a specific width. >+ // use existing line count. fixes bug 9191 >+ if (wHint == SWT.DEFAULT) { >+ width = lineCache.getWidth(); >+ } else { >+ ((WrappedContent) content).wrapLines(width); >+ // caret may be on a different line after a rewrap >+ setCaretLocation(); >+ } >+ if (singleLine == false) { >+ count = content.getLineCount(); >+ } >+ } >+ else { >+ if (singleLine == false) { >+ ((WrappedContent) content).wrapLines(width); >+ // caret may be on a different line after a rewrap >+ setCaretLocation(); >+ count = content.getLineCount(); >+ } >+ } >+ } >+ else if (wHint == SWT.DEFAULT) { >+ // Only calculate what can actually be displayed. >+ // Do this because measuring each text line is a >+ // time-consuming process. >+ int visibleCount = Math.min (count, getDisplay().getBounds().height / lineHeight); >+ lineCache.calculate(0, visibleCount); >+ width = lineCache.getWidth() + leftMargin + rightMargin; >+ } >+ if (hHint != SWT.DEFAULT) { >+ height = hHint; >+ } >+ else { >+ height = count * lineHeight + topMargin + bottomMargin; >+ } >+ // Use default values if no text is defined. >+ if (width == 0) { >+ width = DEFAULT_WIDTH; >+ } >+ if (height == 0) { >+ if (singleLine) { >+ height = lineHeight; >+ } >+ else { >+ height = DEFAULT_HEIGHT; >+ } >+ } >+ Rectangle rect = computeTrim(0, 0, width, height); >+ return new Point (rect.width, rect.height); > } > /** >- * Copies the selected text to the clipboard. The text will be put in the >+ * Copies the selected text to the clipboard. The text will be put in the > * clipboard in plain text format and RTF format. > * <p> > * >@@ -2137,135 +2147,135 @@ > * </ul> > */ > public void copy() { >- checkWidget(); >- int length = selection.y - selection.x; >- if (length > 0) { >- try { >- setClipboardContent(selection.x, length); >- } >- catch (SWTError error) { >- // Copy to clipboard failed. This happens when another application >- // is accessing the clipboard while we copy. Ignore the error. >- // Fixes 1GDQAVN >- // Rethrow all other errors. Fixes bug 17578. >- if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) { >- throw error; >- } >- } >- } >+ checkWidget(); >+ int length = selection.y - selection.x; >+ if (length > 0) { >+ try { >+ setClipboardContent(selection.x, length); >+ } >+ catch (SWTError error) { >+ // Copy to clipboard failed. This happens when another application >+ // is accessing the clipboard while we copy. Ignore the error. >+ // Fixes 1GDQAVN >+ // Rethrow all other errors. Fixes bug 17578. >+ if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) { >+ throw error; >+ } >+ } >+ } > } > /** >- * Returns a string that uses only the line delimiter specified by the >+ * Returns a string that uses only the line delimiter specified by the > * StyledTextContent implementation. > * Returns only the first line if the widget has the SWT.SINGLE style. > * <p> > * >- * @param text the text that may have line delimiters that don't >- * match the model line delimiter. Possible line delimiters >- * are CR ('\r'), LF ('\n'), CR/LF ("\r\n") >- * @return the converted text that only uses the line delimiter >- * specified by the model. Returns only the first line if the widget >- * has the SWT.SINGLE style. >+ * @param text the text that may have line delimiters that don't >+ * match the model line delimiter. Possible line delimiters >+ * are CR ('\r'), LF ('\n'), CR/LF ("\r\n") >+ * @return the converted text that only uses the line delimiter >+ * specified by the model. Returns only the first line if the widget >+ * has the SWT.SINGLE style. > */ > String getModelDelimitedText(String text) { >- StringBuffer convertedText; >- String delimiter = getLineDelimiter(); >- int length = text.length(); >- int crIndex = 0; >- int lfIndex = 0; >- int i = 0; >- >- if (length == 0) { >- return text; >- } >- convertedText = new StringBuffer(length); >- while (i < length) { >- if (crIndex != -1) { >- crIndex = text.indexOf(SWT.CR, i); >- } >- if (lfIndex != -1) { >- lfIndex = text.indexOf(SWT.LF, i); >- } >- if (lfIndex == -1 && crIndex == -1) { // no more line breaks? >- break; >- } >- else // CR occurs before LF or no LF present? >- if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) { >- convertedText.append(text.substring(i, crIndex)); >- if (lfIndex == crIndex + 1) { // CR/LF combination? >- i = lfIndex + 1; >- } >- else { >- i = crIndex + 1; >- } >- } >- else { // LF occurs before CR! >- convertedText.append(text.substring(i, lfIndex)); >- i = lfIndex + 1; >- } >- if (isSingleLine()) { >- break; >- } >- convertedText.append(delimiter); >- } >- // copy remaining text if any and if not in single line mode or no >- // text copied thus far (because there only is one line) >- if (i < length && (isSingleLine() == false || convertedText.length() == 0)) { >- convertedText.append(text.substring(i)); >- } >- return convertedText.toString(); >+ StringBuffer convertedText; >+ String delimiter = getLineDelimiter(); >+ int length = text.length(); >+ int crIndex = 0; >+ int lfIndex = 0; >+ int i = 0; >+ >+ if (length == 0) { >+ return text; >+ } >+ convertedText = new StringBuffer(length); >+ while (i < length) { >+ if (crIndex != -1) { >+ crIndex = text.indexOf(SWT.CR, i); >+ } >+ if (lfIndex != -1) { >+ lfIndex = text.indexOf(SWT.LF, i); >+ } >+ if (lfIndex == -1 && crIndex == -1) { // no more line breaks? >+ break; >+ } >+ else // CR occurs before LF or no LF present? >+ if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) { >+ convertedText.append(text.substring(i, crIndex)); >+ if (lfIndex == crIndex + 1) { // CR/LF combination? >+ i = lfIndex + 1; >+ } >+ else { >+ i = crIndex + 1; >+ } >+ } >+ else { // LF occurs before CR! >+ convertedText.append(text.substring(i, lfIndex)); >+ i = lfIndex + 1; >+ } >+ if (isSingleLine()) { >+ break; >+ } >+ convertedText.append(delimiter); >+ } >+ // copy remaining text if any and if not in single line mode or no >+ // text copied thus far (because there only is one line) >+ if (i < length && (isSingleLine() == false || convertedText.length() == 0)) { >+ convertedText.append(text.substring(i)); >+ } >+ return convertedText.toString(); > } > /** > * Creates default key bindings. > */ > void createKeyBindings() { >- // Navigation >- setKeyBinding(SWT.ARROW_UP, ST.LINE_UP); >- setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN); >- setKeyBinding(SWT.HOME, ST.LINE_START); >- setKeyBinding(SWT.END, ST.LINE_END); >- setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS); >- setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT); >- setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP); >- setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN); >- setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS); >- setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT); >- setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START); >- setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END); >- setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START); >- setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END); >- // Selection >- setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP); >- setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN); >- setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START); >- setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END); >- setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS); >- setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT); >- setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP); >- setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN); >- setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS); >- setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT); >- setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START); >- setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END); >- setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START); >- setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END); >- >- // Modification >- // Cut, Copy, Paste >- setKeyBinding('X' | SWT.MOD1, ST.CUT); >- setKeyBinding('C' | SWT.MOD1, ST.COPY); >- setKeyBinding('V' | SWT.MOD1, ST.PASTE); >- // Cut, Copy, Paste Wordstar style >- setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT); >- setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY); >- setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE); >- setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS); >- >- setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS); >- setKeyBinding(SWT.DEL, ST.DELETE_NEXT); >- >- // Miscellaneous >- setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE); >+ // Navigation >+ setKeyBinding(SWT.ARROW_UP, ST.LINE_UP); >+ setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN); >+ setKeyBinding(SWT.HOME, ST.LINE_START); >+ setKeyBinding(SWT.END, ST.LINE_END); >+ setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS); >+ setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT); >+ setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP); >+ setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN); >+ setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS); >+ setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT); >+ setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START); >+ setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END); >+ setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START); >+ setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END); >+ // Selection >+ setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP); >+ setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN); >+ setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START); >+ setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END); >+ setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS); >+ setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT); >+ setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP); >+ setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN); >+ setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS); >+ setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT); >+ setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START); >+ setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END); >+ setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START); >+ setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END); >+ >+ // Modification >+ // Cut, Copy, Paste >+ setKeyBinding('X' | SWT.MOD1, ST.CUT); >+ setKeyBinding('C' | SWT.MOD1, ST.COPY); >+ setKeyBinding('V' | SWT.MOD1, ST.PASTE); >+ // Cut, Copy, Paste Wordstar style >+ setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT); >+ setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY); >+ setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE); >+ setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS); >+ >+ setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS); >+ setKeyBinding(SWT.DEL, ST.DELETE_NEXT); >+ >+ // Miscellaneous >+ setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE); > } > /** > * Create the bitmaps to use for the caret in bidi mode. This >@@ -2273,37 +2283,37 @@ > * font changes (the caret bitmap height needs to match font height). > */ > void createCaretBitmaps() { >- int caretWidth = BIDI_CARET_WIDTH; >- >- Display display = getDisplay(); >- if (caretPalette == null) { >- caretPalette = new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)}); >- } >- if (leftCaretBitmap != null) { >- leftCaretBitmap.dispose(); >- } >- ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette); >- leftCaretBitmap = new Image(display, imageData); >- GC gc = new GC (leftCaretBitmap); >- gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); >- gc.drawLine(0,0,0,lineHeight); >- gc.drawLine(0,0,caretWidth-1,0); >- gc.drawLine(0,1,1,1); >- gc.dispose(); >- >- if (rightCaretBitmap != null) { >- rightCaretBitmap.dispose(); >- } >- rightCaretBitmap = new Image(display, imageData); >- gc = new GC (rightCaretBitmap); >- gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); >- gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight); >- gc.drawLine(0,0,caretWidth-1,0); >- gc.drawLine(caretWidth-1,1,1,1); >- gc.dispose(); >+ int caretWidth = BIDI_CARET_WIDTH; >+ >+ Display display = getDisplay(); >+ if (caretPalette == null) { >+ caretPalette = new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)}); >+ } >+ if (leftCaretBitmap != null) { >+ leftCaretBitmap.dispose(); >+ } >+ ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette); >+ leftCaretBitmap = new Image(display, imageData); >+ GC gc = new GC (leftCaretBitmap); >+ gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); >+ gc.drawLine(0,0,0,lineHeight); >+ gc.drawLine(0,0,caretWidth-1,0); >+ gc.drawLine(0,1,1,1); >+ gc.dispose(); >+ >+ if (rightCaretBitmap != null) { >+ rightCaretBitmap.dispose(); >+ } >+ rightCaretBitmap = new Image(display, imageData); >+ gc = new GC (rightCaretBitmap); >+ gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); >+ gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight); >+ gc.drawLine(0,0,caretWidth-1,0); >+ gc.drawLine(caretWidth-1,1,1,1); >+ gc.dispose(); > } > /** >- * Moves the selected text to the clipboard. The text will be put in the >+ * Moves the selected text to the clipboard. The text will be put in the > * clipboard in plain text format and RTF format. > * <p> > * >@@ -2313,564 +2323,564 @@ > * </ul> > */ > public void cut(){ >- checkWidget(); >- int length = selection.y - selection.x; >- >- if (length > 0) { >- try { >- setClipboardContent(selection.x, length); >- } >- catch (SWTError error) { >- // Copy to clipboard failed. This happens when another application >- // is accessing the clipboard while we copy. Ignore the error. >- // Fixes 1GDQAVN >- // Rethrow all other errors. Fixes bug 17578. >- if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) { >- throw error; >- } >- // Abort cut operation if copy to clipboard fails. >- // Fixes bug 21030. >- return; >- } >- doDelete(); >- } >+ checkWidget(); >+ int length = selection.y - selection.x; >+ >+ if (length > 0) { >+ try { >+ setClipboardContent(selection.x, length); >+ } >+ catch (SWTError error) { >+ // Copy to clipboard failed. This happens when another application >+ // is accessing the clipboard while we copy. Ignore the error. >+ // Fixes 1GDQAVN >+ // Rethrow all other errors. Fixes bug 17578. >+ if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) { >+ throw error; >+ } >+ // Abort cut operation if copy to clipboard fails. >+ // Fixes bug 21030. >+ return; >+ } >+ doDelete(); >+ } > } >-/** >+/** > * A mouse move event has occurred. See if we should start autoscrolling. If >- * the move position is outside of the client area, initiate autoscrolling. >+ * the move position is outside of the client area, initiate autoscrolling. > * Otherwise, we've moved back into the widget so end autoscrolling. > */ > void doAutoScroll(Event event) { >- Rectangle area = getClientArea(); >- >- if (event.y > area.height) { >- doAutoScroll(SWT.DOWN); >- } >- else >- if (event.y < 0) { >- doAutoScroll(SWT.UP); >- } >- else >- if (event.x < leftMargin && wordWrap == false) { >- doAutoScroll(SWT.LEFT); >- } >- else >- if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) { >- doAutoScroll(SWT.RIGHT); >- } >- else { >- endAutoScroll(); >- } >+ Rectangle area = getClientArea(); >+ >+ if (event.y > area.height) { >+ doAutoScroll(SWT.DOWN); >+ } >+ else >+ if (event.y < 0) { >+ doAutoScroll(SWT.UP); >+ } >+ else >+ if (event.x < leftMargin && wordWrap == false) { >+ doAutoScroll(SWT.LEFT); >+ } >+ else >+ if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) { >+ doAutoScroll(SWT.RIGHT); >+ } >+ else { >+ endAutoScroll(); >+ } > } >-/** >+/** > * Initiates autoscrolling. > * <p> > * > * @param direction SWT.UP, SWT.DOWN, SWT.RIGHT, SWT.LEFT > */ > void doAutoScroll(int direction) { >- Runnable timer = null; >- final int TIMER_INTERVAL = 5; >- >- // If we're already autoscrolling in the given direction do nothing >- if (autoScrollDirection == direction) { >- return; >- } >- >- final Display display = getDisplay(); >- // Set a timer that will simulate the user pressing and holding >- // down a cursor key (i.e., arrowUp, arrowDown). >- if (direction == SWT.UP) { >- timer = new Runnable() { >- public void run() { >- if (autoScrollDirection == SWT.UP) { >- doSelectionLineUp(); >- display.timerExec(TIMER_INTERVAL, this); >- } >- } >- }; >- } else if (direction == SWT.DOWN) { >- timer = new Runnable() { >- public void run() { >- if (autoScrollDirection == SWT.DOWN) { >- doSelectionLineDown(); >- display.timerExec(TIMER_INTERVAL, this); >- } >- } >- }; >- } else if (direction == SWT.RIGHT) { >- timer = new Runnable() { >- public void run() { >- if (autoScrollDirection == SWT.RIGHT) { >- doColumnRight(); >- setMouseWordSelectionAnchor(); >- doSelection(SWT.RIGHT); >- display.timerExec(TIMER_INTERVAL, this); >- } >- } >- }; >- } else if (direction == SWT.LEFT) { >- timer = new Runnable() { >- public void run() { >- if (autoScrollDirection == SWT.LEFT) { >- doColumnLeft(); >- setMouseWordSelectionAnchor(); >- doSelection(SWT.LEFT); >- display.timerExec(TIMER_INTERVAL, this); >- } >- } >- }; >- } >- if (timer != null) { >- autoScrollDirection = direction; >- display.timerExec(TIMER_INTERVAL, timer); >- } >+ Runnable timer = null; >+ final int TIMER_INTERVAL = 5; >+ >+ // If we're already autoscrolling in the given direction do nothing >+ if (autoScrollDirection == direction) { >+ return; >+ } >+ >+ final Display display = getDisplay(); >+ // Set a timer that will simulate the user pressing and holding >+ // down a cursor key (i.e., arrowUp, arrowDown). >+ if (direction == SWT.UP) { >+ timer = new Runnable() { >+ public void run() { >+ if (autoScrollDirection == SWT.UP) { >+ doSelectionLineUp(); >+ display.timerExec(TIMER_INTERVAL, this); >+ } >+ } >+ }; >+ } else if (direction == SWT.DOWN) { >+ timer = new Runnable() { >+ public void run() { >+ if (autoScrollDirection == SWT.DOWN) { >+ doSelectionLineDown(); >+ display.timerExec(TIMER_INTERVAL, this); >+ } >+ } >+ }; >+ } else if (direction == SWT.RIGHT) { >+ timer = new Runnable() { >+ public void run() { >+ if (autoScrollDirection == SWT.RIGHT) { >+ doColumnRight(); >+ setMouseWordSelectionAnchor(); >+ doSelection(SWT.RIGHT); >+ display.timerExec(TIMER_INTERVAL, this); >+ } >+ } >+ }; >+ } else if (direction == SWT.LEFT) { >+ timer = new Runnable() { >+ public void run() { >+ if (autoScrollDirection == SWT.LEFT) { >+ doColumnLeft(); >+ setMouseWordSelectionAnchor(); >+ doSelection(SWT.LEFT); >+ display.timerExec(TIMER_INTERVAL, this); >+ } >+ } >+ }; >+ } >+ if (timer != null) { >+ autoScrollDirection = direction; >+ display.timerExec(TIMER_INTERVAL, timer); >+ } > } > /** > * Deletes the previous character. Delete the selected text if any. > * Move the caret in front of the deleted text. > */ > void doBackspace() { >- Event event = new Event(); >- event.text = ""; >- if (selection.x != selection.y) { >- event.start = selection.x; >- event.end = selection.y; >- sendKeyEvent(event); >- } >- else >- if (caretOffset > 0) { >- int line = content.getLineAtOffset(caretOffset); >- int lineOffset = content.getOffsetAtLine(line); >- >- if (caretOffset == lineOffset) { >- lineOffset = content.getOffsetAtLine(line - 1); >- event.start = lineOffset + content.getLine(line - 1).length(); >- event.end = caretOffset; >- } >- else { >- event.start = caretOffset - 1; >- event.end = caretOffset; >- } >- sendKeyEvent(event); >- } >+ Event event = new Event(); >+ event.text = ""; >+ if (selection.x != selection.y) { >+ event.start = selection.x; >+ event.end = selection.y; >+ sendKeyEvent(event); >+ } >+ else >+ if (caretOffset > 0) { >+ int line = content.getLineAtOffset(caretOffset); >+ int lineOffset = content.getOffsetAtLine(line); >+ >+ if (caretOffset == lineOffset) { >+ lineOffset = content.getOffsetAtLine(line - 1); >+ event.start = lineOffset + content.getLine(line - 1).length(); >+ event.end = caretOffset; >+ } >+ else { >+ event.start = caretOffset - 1; >+ event.end = caretOffset; >+ } >+ sendKeyEvent(event); >+ } > } > /** > * Moves the caret one character to the left. Do not go to the previous line. >- * When in a bidi locale and at a R2L character the caret is moved to the >- * beginning of the R2L segment (visually right) and then one character to the >+ * When in a bidi locale and at a R2L character the caret is moved to the >+ * beginning of the R2L segment (visually right) and then one character to the > * left (visually left because it's now in a L2R segment). > */ > void doColumnLeft() { >- int line = content.getLineAtOffset(caretOffset); >- int lineOffset = content.getOffsetAtLine(line); >- int offsetInLine = caretOffset - lineOffset; >- >- if (isBidi()) { >- String lineText = content.getLine(line); >- int lineLength = lineText.length(); >- GC gc = getGC(); >- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >- >- if (horizontalScrollOffset > 0 || offsetInLine > 0) { >- if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) { >- // advance caret logically if in R2L segment (move visually left) >- caretOffset++; >- doSelection(SWT.RIGHT); >- if (caretOffset - lineOffset == lineLength) { >- // if the line end is reached in a R2L segment, make the >- // caret position (visual left border) visible before >- // jumping to segment start >- showCaret(); >- } >- // end of R2L segment reached (visual left side)? >- if (bidi.isRightToLeft(caretOffset - lineOffset) == false) { >- if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) { >- // make beginning of R2L segment visible before going >- // left, to L2R segment important if R2L segment ends >- // at visual left in order to scroll all the way to the >- // left. Fixes 1GKM3XS >- showCaret(); >- } >- // go to beginning of R2L segment (visually end of next L2R >- // segment)/beginning of line >- caretOffset--; >- while (caretOffset - lineOffset > 0 && >- bidi.isRightToLeft(caretOffset - lineOffset)) { >- caretOffset--; >- } >- } >- } >- else >- if (offsetInLine == lineLength && >- bidi.getTextPosition(lineLength) != XINSET) { >- // at logical line end in R2L segment but there's more text (a >- // L2R segment) go to end of R2L segment (visually left of next >- // L2R segment)/end of line >- caretOffset--; >- while (caretOffset - lineOffset > 0 && >- bidi.isRightToLeft(caretOffset - lineOffset)) { >- caretOffset--; >- } >- } >- else >- if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) { >- // decrease caret logically if in L2R segment (move visually left) >- caretOffset--; >- doSelection(SWT.LEFT); >- // end of L2R segment reached (visual left side of preceeding R2L >- // segment)? >- if (caretOffset - lineOffset > 0 && >- bidi.isRightToLeft(caretOffset - lineOffset - 1)) { >- // go to beginning of R2L segment (visually start of next L2R >- // segment)/beginning of line >- caretOffset--; >- while (caretOffset - lineOffset > 0 && >- bidi.isRightToLeft(caretOffset - lineOffset - 1)) { >- caretOffset--; >- } >- } >- } >- // if new caret position is to the left of the client area >- if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) { >- // scroll to the caret position >- showCaret(); >- } >- else { >- // otherwise just update caret position without scrolling it into view >- setBidiCaretLocation(null); >- setBidiKeyboardLanguage(); >- } >- // Beginning of line reached (auto scroll finished) but not scrolled >- // completely to the left? Fixes 1GKM193 >- if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 && >- horizontalScrollOffset <= XINSET) { >- scrollHorizontalBar(-horizontalScrollOffset); >- } >- } >- gc.dispose(); >- } >- else >- if (offsetInLine > 0) { >- caretOffset--; >- showCaret(); >- } >+ int line = content.getLineAtOffset(caretOffset); >+ int lineOffset = content.getOffsetAtLine(line); >+ int offsetInLine = caretOffset - lineOffset; >+ >+ if (isBidi()) { >+ String lineText = content.getLine(line); >+ int lineLength = lineText.length(); >+ GC gc = getGC(); >+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >+ >+ if (horizontalScrollOffset > 0 || offsetInLine > 0) { >+ if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) { >+ // advance caret logically if in R2L segment (move visually left) >+ caretOffset++; >+ doSelection(SWT.RIGHT); >+ if (caretOffset - lineOffset == lineLength) { >+ // if the line end is reached in a R2L segment, make the >+ // caret position (visual left border) visible before >+ // jumping to segment start >+ showCaret(); >+ } >+ // end of R2L segment reached (visual left side)? >+ if (bidi.isRightToLeft(caretOffset - lineOffset) == false) { >+ if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) { >+ // make beginning of R2L segment visible before going >+ // left, to L2R segment important if R2L segment ends >+ // at visual left in order to scroll all the way to the >+ // left. Fixes 1GKM3XS >+ showCaret(); >+ } >+ // go to beginning of R2L segment (visually end of next L2R >+ // segment)/beginning of line >+ caretOffset--; >+ while (caretOffset - lineOffset > 0 && >+ bidi.isRightToLeft(caretOffset - lineOffset)) { >+ caretOffset--; >+ } >+ } >+ } >+ else >+ if (offsetInLine == lineLength && >+ bidi.getTextPosition(lineLength) != XINSET) { >+ // at logical line end in R2L segment but there's more text (a >+ // L2R segment) go to end of R2L segment (visually left of next >+ // L2R segment)/end of line >+ caretOffset--; >+ while (caretOffset - lineOffset > 0 && >+ bidi.isRightToLeft(caretOffset - lineOffset)) { >+ caretOffset--; >+ } >+ } >+ else >+ if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) { >+ // decrease caret logically if in L2R segment (move visually left) >+ caretOffset--; >+ doSelection(SWT.LEFT); >+ // end of L2R segment reached (visual left side of preceeding R2L >+ // segment)? >+ if (caretOffset - lineOffset > 0 && >+ bidi.isRightToLeft(caretOffset - lineOffset - 1)) { >+ // go to beginning of R2L segment (visually start of next L2R >+ // segment)/beginning of line >+ caretOffset--; >+ while (caretOffset - lineOffset > 0 && >+ bidi.isRightToLeft(caretOffset - lineOffset - 1)) { >+ caretOffset--; >+ } >+ } >+ } >+ // if new caret position is to the left of the client area >+ if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) { >+ // scroll to the caret position >+ showCaret(); >+ } >+ else { >+ // otherwise just update caret position without scrolling it into view >+ setBidiCaretLocation(null); >+ setBidiKeyboardLanguage(); >+ } >+ // Beginning of line reached (auto scroll finished) but not scrolled >+ // completely to the left? Fixes 1GKM193 >+ if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 && >+ horizontalScrollOffset <= XINSET) { >+ scrollHorizontalBar(-horizontalScrollOffset); >+ } >+ } >+ gc.dispose(); >+ } >+ else >+ if (offsetInLine > 0) { >+ caretOffset--; >+ showCaret(); >+ } > } > /** > * Moves the caret one character to the right. Do not go to the next line. >- * When in a bidi locale and at a R2L character the caret is moved to the >- * end of the R2L segment (visually left) and then one character to the >+ * When in a bidi locale and at a R2L character the caret is moved to the >+ * end of the R2L segment (visually left) and then one character to the > * right (visually right because it's now in a L2R segment). > */ > void doColumnRight() { >- int line = content.getLineAtOffset(caretOffset); >- int lineOffset = content.getOffsetAtLine(line); >- int offsetInLine = caretOffset - lineOffset; >- String lineText = content.getLine(line); >- int lineLength = lineText.length(); >- >- if (isBidi()) { >- GC gc = getGC(); >- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >- if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || >- offsetInLine < lineLength) { >- if (bidi.isRightToLeft(offsetInLine) == false && >- offsetInLine < lineLength) { >- // advance caret logically if in L2R segment (move visually right) >- caretOffset++; >- doSelection(SWT.RIGHT); >- // end of L2R segment reached (visual right side)? >- if (bidi.isRightToLeft(caretOffset - lineOffset)) { >- // go to end of R2L segment (visually left of next R2L segment)/ >- // end of line >- caretOffset++; >- while (caretOffset < lineOffset + lineLength && >- bidi.isRightToLeft(caretOffset - lineOffset)) { >- caretOffset++; >- } >- } >- } >- else >- if (offsetInLine > 0 && >- (bidi.isRightToLeft(offsetInLine) || >- bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || >- offsetInLine < lineLength)) { >- // advance caret visually if in R2L segment or logically at line end >- // but right end of line is not fully visible yet >- caretOffset--; >- doSelection(SWT.LEFT); >- offsetInLine = caretOffset - lineOffset; >- // end of R2L segment reached (visual right side)? >- if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) { >- // go to end of R2L segment (visually left of next L2R segment)/ >- // end of line >- caretOffset++; >- while (caretOffset < lineOffset + lineLength && >- bidi.isRightToLeft(caretOffset - lineOffset)) { >- caretOffset++; >- } >- } >- } >- else >- if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) { >- // at logical line start in R2L segment but there's more text (a L2R >- // segment) go to end of R2L segment (visually left of next L2R >- // segment)/end of line >- caretOffset++; >- while (caretOffset < lineOffset + lineLength && >- bidi.isRightToLeft(caretOffset - lineOffset - 1)) { >- caretOffset++; >- } >- } >- offsetInLine = caretOffset - lineOffset; >- // if new caret position is to the right of the client area >- if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) { >- // scroll to the caret position >- showCaret(); >- } >- else { >- // otherwise just update caret position without scrolling it into view >- setBidiCaretLocation(null); >- setBidiKeyboardLanguage(); >- } >- if (offsetInLine > 0 && offsetInLine < lineLength - 1) { >- int clientAreaEnd = horizontalScrollOffset + getClientArea().width; >- boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine); >- int textWidth = bidi.getTextWidth() + leftMargin; >- // between L2R and R2L segment and second character of R2L segment is >- // left of right border and logical line end is left of right border >- // but visual line end is not left of right border >- if (directionChange && >- bidi.isRightToLeft(offsetInLine + 1) && >- bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd && >- bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) { >- // make visual line end visible >- scrollHorizontalBar(textWidth - clientAreaEnd); >- } >- } >- } >- gc.dispose(); >- } >- else >- if (offsetInLine < lineLength) { >- caretOffset++; >- showCaret(); >- } >+ int line = content.getLineAtOffset(caretOffset); >+ int lineOffset = content.getOffsetAtLine(line); >+ int offsetInLine = caretOffset - lineOffset; >+ String lineText = content.getLine(line); >+ int lineLength = lineText.length(); >+ >+ if (isBidi()) { >+ GC gc = getGC(); >+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >+ if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || >+ offsetInLine < lineLength) { >+ if (bidi.isRightToLeft(offsetInLine) == false && >+ offsetInLine < lineLength) { >+ // advance caret logically if in L2R segment (move visually right) >+ caretOffset++; >+ doSelection(SWT.RIGHT); >+ // end of L2R segment reached (visual right side)? >+ if (bidi.isRightToLeft(caretOffset - lineOffset)) { >+ // go to end of R2L segment (visually left of next R2L segment)/ >+ // end of line >+ caretOffset++; >+ while (caretOffset < lineOffset + lineLength && >+ bidi.isRightToLeft(caretOffset - lineOffset)) { >+ caretOffset++; >+ } >+ } >+ } >+ else >+ if (offsetInLine > 0 && >+ (bidi.isRightToLeft(offsetInLine) || >+ bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width || >+ offsetInLine < lineLength)) { >+ // advance caret visually if in R2L segment or logically at line end >+ // but right end of line is not fully visible yet >+ caretOffset--; >+ doSelection(SWT.LEFT); >+ offsetInLine = caretOffset - lineOffset; >+ // end of R2L segment reached (visual right side)? >+ if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) { >+ // go to end of R2L segment (visually left of next L2R segment)/ >+ // end of line >+ caretOffset++; >+ while (caretOffset < lineOffset + lineLength && >+ bidi.isRightToLeft(caretOffset - lineOffset)) { >+ caretOffset++; >+ } >+ } >+ } >+ else >+ if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) { >+ // at logical line start in R2L segment but there's more text (a L2R >+ // segment) go to end of R2L segment (visually left of next L2R >+ // segment)/end of line >+ caretOffset++; >+ while (caretOffset < lineOffset + lineLength && >+ bidi.isRightToLeft(caretOffset - lineOffset - 1)) { >+ caretOffset++; >+ } >+ } >+ offsetInLine = caretOffset - lineOffset; >+ // if new caret position is to the right of the client area >+ if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) { >+ // scroll to the caret position >+ showCaret(); >+ } >+ else { >+ // otherwise just update caret position without scrolling it into view >+ setBidiCaretLocation(null); >+ setBidiKeyboardLanguage(); >+ } >+ if (offsetInLine > 0 && offsetInLine < lineLength - 1) { >+ int clientAreaEnd = horizontalScrollOffset + getClientArea().width; >+ boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine); >+ int textWidth = bidi.getTextWidth() + leftMargin; >+ // between L2R and R2L segment and second character of R2L segment is >+ // left of right border and logical line end is left of right border >+ // but visual line end is not left of right border >+ if (directionChange && >+ bidi.isRightToLeft(offsetInLine + 1) && >+ bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd && >+ bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) { >+ // make visual line end visible >+ scrollHorizontalBar(textWidth - clientAreaEnd); >+ } >+ } >+ } >+ gc.dispose(); >+ } >+ else >+ if (offsetInLine < lineLength) { >+ caretOffset++; >+ showCaret(); >+ } > } > /** >- * Replaces the selection with the character or insert the character at the >+ * Replaces the selection with the character or insert the character at the > * current caret position if no selection exists. >- * If a carriage return was typed replace it with the line break character >+ * If a carriage return was typed replace it with the line break character > * used by the widget on this platform. > * <p> > * > * @param key the character typed by the user > */ > void doContent(char key) { >- Event event; >- >- if (textLimit > 0 && >- content.getCharCount() - (selection.y - selection.x) >= textLimit) { >- return; >- } >- event = new Event(); >- event.start = selection.x; >- event.end = selection.y; >- // replace a CR line break with the widget line break >- // CR does not make sense on Windows since most (all?) applications >- // don't recognize CR as a line break. >- if (key == SWT.CR || key == SWT.LF) { >- if (isSingleLine() == false) { >- event.text = getLineDelimiter(); >- } >- } >- // no selection and overwrite mode is on and the typed key is not a >- // tab character (tabs are always inserted without overwriting)? >- else >- if (selection.x == selection.y && overwrite == true && key != TAB) { >- int lineIndex = content.getLineAtOffset(event.end); >- int lineOffset = content.getOffsetAtLine(lineIndex); >- String line = content.getLine(lineIndex); >- // replace character at caret offset if the caret is not at the >- // end of the line >- if (event.end < lineOffset + line.length()) { >- event.end++; >- } >- event.text = new String(new char[] {key}); >- } >- else { >- event.text = new String(new char[] {key}); >- } >- if (event.text != null) { >- sendKeyEvent(event); >- } >+ Event event; >+ >+ if (textLimit > 0 && >+ content.getCharCount() - (selection.y - selection.x) >= textLimit) { >+ return; >+ } >+ event = new Event(); >+ event.start = selection.x; >+ event.end = selection.y; >+ // replace a CR line break with the widget line break >+ // CR does not make sense on Windows since most (all?) applications >+ // don't recognize CR as a line break. >+ if (key == SWT.CR || key == SWT.LF) { >+ if (isSingleLine() == false) { >+ event.text = getLineDelimiter(); >+ } >+ } >+ // no selection and overwrite mode is on and the typed key is not a >+ // tab character (tabs are always inserted without overwriting)? >+ else >+ if (selection.x == selection.y && overwrite == true && key != TAB) { >+ int lineIndex = content.getLineAtOffset(event.end); >+ int lineOffset = content.getOffsetAtLine(lineIndex); >+ String line = content.getLine(lineIndex); >+ // replace character at caret offset if the caret is not at the >+ // end of the line >+ if (event.end < lineOffset + line.length()) { >+ event.end++; >+ } >+ event.text = new String(new char[] {key}); >+ } >+ else { >+ event.text = new String(new char[] {key}); >+ } >+ if (event.text != null) { >+ sendKeyEvent(event); >+ } > } > /** > * Moves the caret after the last character of the widget content. > */ > void doContentEnd() { >- // place caret at end of first line if receiver is in single >- // line mode. fixes 4820. >- if (isSingleLine()) { >- doLineEnd(); >- } >- else { >- int length = content.getCharCount(); >- if (caretOffset < length) { >- caretOffset = length; >- showCaret(); >- } >- } >+ // place caret at end of first line if receiver is in single >+ // line mode. fixes 4820. >+ if (isSingleLine()) { >+ doLineEnd(); >+ } >+ else { >+ int length = content.getCharCount(); >+ if (caretOffset < length) { >+ caretOffset = length; >+ showCaret(); >+ } >+ } > } > /** > * Moves the caret in front of the first character of the widget content. > */ > void doContentStart() { >- if (caretOffset > 0) { >- caretOffset = 0; >- showCaret(); >- } >+ if (caretOffset > 0) { >+ caretOffset = 0; >+ showCaret(); >+ } > } > /** > * Moves the caret to the start of the selection if a selection exists. >- * Otherwise, if no selection exists move the cursor according to the >+ * Otherwise, if no selection exists move the cursor according to the > * cursor selection rules. > * <p> > * > * @see #doSelectionCursorPrevious > */ > void doCursorPrevious() { >- if (selection.y - selection.x > 0) { >- int caretLine; >- >- caretOffset = selection.x; >- caretLine = getCaretLine(); >- showCaret(caretLine); >- } >- else { >- doSelectionCursorPrevious(); >- } >+ if (selection.y - selection.x > 0) { >+ int caretLine; >+ >+ caretOffset = selection.x; >+ caretLine = getCaretLine(); >+ showCaret(caretLine); >+ } >+ else { >+ doSelectionCursorPrevious(); >+ } > } > /** > * Moves the caret to the end of the selection if a selection exists. >- * Otherwise, if no selection exists move the cursor according to the >+ * Otherwise, if no selection exists move the cursor according to the > * cursor selection rules. > * <p> > * > * @see #doSelectionCursorNext > */ > void doCursorNext() { >- if (selection.y - selection.x > 0) { >- int caretLine; >+ if (selection.y - selection.x > 0) { >+ int caretLine; > >- caretOffset = selection.y; >- caretLine = getCaretLine(); >- showCaret(caretLine); >- } >- else { >- doSelectionCursorNext(); >- } >+ caretOffset = selection.y; >+ caretLine = getCaretLine(); >+ showCaret(caretLine); >+ } >+ else { >+ doSelectionCursorNext(); >+ } > } > /** > * Deletes the next character. Delete the selected text if any. > */ > void doDelete() { >- Event event = new Event(); >- >- event.text = ""; >- if (selection.x != selection.y) { >- event.start = selection.x; >- event.end = selection.y; >- sendKeyEvent(event); >- } >- else >- if (caretOffset < content.getCharCount()) { >- int line = content.getLineAtOffset(caretOffset); >- int lineOffset = content.getOffsetAtLine(line); >- int lineLength = content.getLine(line).length(); >- >- if (caretOffset == lineOffset + lineLength) { >- event.start = caretOffset; >- event.end = content.getOffsetAtLine(line + 1); >- } >- else { >- event.start = caretOffset; >- event.end = caretOffset + 1; >- } >- sendKeyEvent(event); >- } >+ Event event = new Event(); >+ >+ event.text = ""; >+ if (selection.x != selection.y) { >+ event.start = selection.x; >+ event.end = selection.y; >+ sendKeyEvent(event); >+ } >+ else >+ if (caretOffset < content.getCharCount()) { >+ int line = content.getLineAtOffset(caretOffset); >+ int lineOffset = content.getOffsetAtLine(line); >+ int lineLength = content.getLine(line).length(); >+ >+ if (caretOffset == lineOffset + lineLength) { >+ event.start = caretOffset; >+ event.end = content.getOffsetAtLine(line + 1); >+ } >+ else { >+ event.start = caretOffset; >+ event.end = caretOffset + 1; >+ } >+ sendKeyEvent(event); >+ } > } > /** >- * Moves the caret one line down and to the same character offset relative >- * to the beginning of the line. Move the caret to the end of the new line >+ * Moves the caret one line down and to the same character offset relative >+ * to the beginning of the line. Move the caret to the end of the new line > * if the new line is shorter than the character offset. >- * >+ * > * @return index of the new line relative to the first line in the document > */ > int doLineDown() { >- if (isSingleLine()) { >- return 0; >- } >- // allow line down action only if receiver is not in single line mode. >- // fixes 4820. >- int caretLine = getCaretLine(); >- if (caretLine < content.getLineCount() - 1) { >- caretLine++; >- if (isBidi()) { >- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >- } >- else { >- caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >- } >- } >- return caretLine; >+ if (isSingleLine()) { >+ return 0; >+ } >+ // allow line down action only if receiver is not in single line mode. >+ // fixes 4820. >+ int caretLine = getCaretLine(); >+ if (caretLine < content.getLineCount() - 1) { >+ caretLine++; >+ if (isBidi()) { >+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >+ } >+ else { >+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >+ } >+ } >+ return caretLine; > } > /** > * Moves the caret to the end of the line. > */ > void doLineEnd() { >- int caretLine = getCaretLine(); >- int lineOffset = content.getOffsetAtLine(caretLine); >- int lineLength = content.getLine(caretLine).length(); >- int lineEndOffset = lineOffset + lineLength; >- >- if (caretOffset < lineEndOffset) { >- caretOffset = lineEndOffset; >- showCaret(); >- } >+ int caretLine = getCaretLine(); >+ int lineOffset = content.getOffsetAtLine(caretLine); >+ int lineLength = content.getLine(caretLine).length(); >+ int lineEndOffset = lineOffset + lineLength; >+ >+ if (caretOffset < lineEndOffset) { >+ caretOffset = lineEndOffset; >+ showCaret(); >+ } > } > /** > * Moves the caret to the beginning of the line. > */ > void doLineStart() { >- int caretLine = getCaretLine(); >- int lineOffset = content.getOffsetAtLine(caretLine); >- >- if (caretOffset > lineOffset) { >- caretOffset = lineOffset; >- showCaret(caretLine); >- } >+ int caretLine = getCaretLine(); >+ int lineOffset = content.getOffsetAtLine(caretLine); >+ >+ if (caretOffset > lineOffset) { >+ caretOffset = lineOffset; >+ showCaret(caretLine); >+ } > } > /** >- * Moves the caret one line up and to the same character offset relative >- * to the beginning of the line. Move the caret to the end of the new line >+ * Moves the caret one line up and to the same character offset relative >+ * to the beginning of the line. Move the caret to the end of the new line > * if the new line is shorter than the character offset. >- * >+ * > * @return index of the new line relative to the first line in the document > */ > int doLineUp() { >- int caretLine = getCaretLine(); >+ int caretLine = getCaretLine(); > >- if (caretLine > 0) { >- caretLine--; >- if (isBidi()) { >- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >- } >- else { >- caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >- } >- } >- return caretLine; >+ if (caretLine > 0) { >+ caretLine--; >+ if (isBidi()) { >+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >+ } >+ else { >+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >+ } >+ } >+ return caretLine; > } > /** > * Moves the caret to the specified location. >@@ -2879,370 +2889,370 @@ > * @param x x location of the new caret position > * @param y y location of the new caret position > * @param select the location change is a selection operation. >- * include the line delimiter in the selection >+ * include the line delimiter in the selection > */ > void doMouseLocationChange(int x, int y, boolean select) { >- int line = (y + verticalScrollOffset) / lineHeight; >- int lineCount = content.getLineCount(); >- int newCaretOffset; >- int newCaretLine; >- >- if (line > lineCount - 1) { >- line = lineCount - 1; >- } >- // allow caret to be placed below first line only if receiver is >- // not in single line mode. fixes 4820. >- if (line < 0 || (isSingleLine() && line > 0)) { >- return; >- } >- if (isBidi()) { >- newCaretOffset = getBidiOffsetAtMouseLocation(x, line); >- } >- else { >- newCaretOffset = getOffsetAtMouseLocation(x, line); >- } >- if (mouseDoubleClick) { >- // double click word select the previous/next word. fixes bug 15610 >- newCaretOffset = doMouseWordSelect(x, newCaretOffset, line); >- } >- newCaretLine = content.getLineAtOffset(newCaretOffset); >- // Is the mouse within the left client area border or on >- // a different line? If not the autoscroll selection >- // could be incorrectly reset. Fixes 1GKM3XS >- if (y >= 0 && y < getClientArea().height && >- (x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) { >- if (newCaretOffset != caretOffset) { >- caretOffset = newCaretOffset; >- if (select) { >- doMouseSelection(); >- } >- showCaret(); >- } >- } >- if (select == false) { >- clearSelection(true); >- } >+ int line = (y + verticalScrollOffset) / lineHeight; >+ int lineCount = content.getLineCount(); >+ int newCaretOffset; >+ int newCaretLine; >+ >+ if (line > lineCount - 1) { >+ line = lineCount - 1; >+ } >+ // allow caret to be placed below first line only if receiver is >+ // not in single line mode. fixes 4820. >+ if (line < 0 || (isSingleLine() && line > 0)) { >+ return; >+ } >+ if (isBidi()) { >+ newCaretOffset = getBidiOffsetAtMouseLocation(x, line); >+ } >+ else { >+ newCaretOffset = getOffsetAtMouseLocation(x, line); >+ } >+ if (mouseDoubleClick) { >+ // double click word select the previous/next word. fixes bug 15610 >+ newCaretOffset = doMouseWordSelect(x, newCaretOffset, line); >+ } >+ newCaretLine = content.getLineAtOffset(newCaretOffset); >+ // Is the mouse within the left client area border or on >+ // a different line? If not the autoscroll selection >+ // could be incorrectly reset. Fixes 1GKM3XS >+ if (y >= 0 && y < getClientArea().height && >+ (x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) { >+ if (newCaretOffset != caretOffset) { >+ caretOffset = newCaretOffset; >+ if (select) { >+ doMouseSelection(); >+ } >+ showCaret(); >+ } >+ } >+ if (select == false) { >+ clearSelection(true); >+ } > } > /** > * Updates the selection based on the caret position > */ > void doMouseSelection() { >- if (caretOffset <= selection.x || >- (caretOffset > selection.x && >- caretOffset < selection.y && selectionAnchor == selection.x)) { >- doSelection(SWT.LEFT); >- } >- else { >- doSelection(SWT.RIGHT); >- } >+ if (caretOffset <= selection.x || >+ (caretOffset > selection.x && >+ caretOffset < selection.y && selectionAnchor == selection.x)) { >+ doSelection(SWT.LEFT); >+ } >+ else { >+ doSelection(SWT.RIGHT); >+ } > } > /** >- * Returns the offset of the word at the specified offset. >- * If the current selection extends from high index to low index >- * (i.e., right to left, or caret is at left border of selecton on >+ * Returns the offset of the word at the specified offset. >+ * If the current selection extends from high index to low index >+ * (i.e., right to left, or caret is at left border of selecton on > * non-bidi platforms) the start offset of the word preceeding the >- * selection is returned. If the current selection extends from >- * low index to high index the end offset of the word following >+ * selection is returned. If the current selection extends from >+ * low index to high index the end offset of the word following > * the selection is returned. >- * >+ * > * @param x mouse x location > * @param newCaretOffset caret offset of the mouse cursor location > * @param line line index of the mouse cursor location > */ > int doMouseWordSelect(int x, int newCaretOffset, int line) { >- int wordOffset; >+ int wordOffset; > >- // flip selection anchor based on word selection direction from >- // base double click. Always do this here (and don't rely on doAutoScroll) >- // because auto scroll only does not cover all possible mouse selections >- // (e.g., mouse x < 0 && mouse y > caret line y) >- if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) { >- selectionAnchor = doubleClickSelection.y; >- } >- else >- if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) { >- selectionAnchor = doubleClickSelection.x; >- } >- if (x >= 0 && x < getClientArea().width) { >- // find the previous/next word >- if (caretOffset == selection.x) { >- wordOffset = getWordStart(newCaretOffset); >- } >- else { >- wordOffset = getWordEndNoSpaces(newCaretOffset); >- } >- // mouse word select only on same line mouse cursor is on >- if (content.getLineAtOffset(wordOffset) == line) { >- newCaretOffset = wordOffset; >- } >- } >- return newCaretOffset; >+ // flip selection anchor based on word selection direction from >+ // base double click. Always do this here (and don't rely on doAutoScroll) >+ // because auto scroll only does not cover all possible mouse selections >+ // (e.g., mouse x < 0 && mouse y > caret line y) >+ if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) { >+ selectionAnchor = doubleClickSelection.y; >+ } >+ else >+ if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) { >+ selectionAnchor = doubleClickSelection.x; >+ } >+ if (x >= 0 && x < getClientArea().width) { >+ // find the previous/next word >+ if (caretOffset == selection.x) { >+ wordOffset = getWordStart(newCaretOffset); >+ } >+ else { >+ wordOffset = getWordEndNoSpaces(newCaretOffset); >+ } >+ // mouse word select only on same line mouse cursor is on >+ if (content.getLineAtOffset(wordOffset) == line) { >+ newCaretOffset = wordOffset; >+ } >+ } >+ return newCaretOffset; > } > /** > * Scrolls one page down so that the last line (truncated or whole) > * of the current page becomes the fully visible top line. >- * The caret is scrolled the same number of lines so that its location >- * relative to the top line remains the same. The exception is the end >- * of the text where a full page scroll is not possible. In this case >+ * The caret is scrolled the same number of lines so that its location >+ * relative to the top line remains the same. The exception is the end >+ * of the text where a full page scroll is not possible. In this case > * the caret is moved after the last character. > * <p> > * > * @param select whether or not to select the page > */ > void doPageDown(boolean select) { >- int lineCount = content.getLineCount(); >- int oldColumnX = columnX; >- int caretLine; >- >- // do nothing if in single line mode. fixes 5673 >- if (isSingleLine()) { >- return; >- } >- caretLine = getCaretLine(); >- if (caretLine < lineCount - 1) { >- int verticalMaximum = lineCount * getVerticalIncrement(); >- int pageSize = getClientArea().height; >- int scrollLines = Math.min(lineCount - caretLine - 1, getLineCountWhole()); >- int scrollOffset; >- >- // ensure that scrollLines never gets negative and at leat one >- // line is scrolled. fixes bug 5602. >- scrollLines = Math.max(1, scrollLines); >- caretLine += scrollLines; >- if (isBidi()) { >- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >- } >- else { >- caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >- } >- if (select) { >- doSelection(SWT.RIGHT); >- } >- // scroll one page down or to the bottom >- scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement(); >- if (scrollOffset + pageSize > verticalMaximum) { >- scrollOffset = verticalMaximum - pageSize; >- } >- if (scrollOffset > verticalScrollOffset) { >- setVerticalScrollOffset(scrollOffset, true); >- } >- } >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- // save the original horizontal caret position >- columnX = oldColumnX; >+ int lineCount = content.getLineCount(); >+ int oldColumnX = columnX; >+ int caretLine; >+ >+ // do nothing if in single line mode. fixes 5673 >+ if (isSingleLine()) { >+ return; >+ } >+ caretLine = getCaretLine(); >+ if (caretLine < lineCount - 1) { >+ int verticalMaximum = lineCount * getVerticalIncrement(); >+ int pageSize = getClientArea().height; >+ int scrollLines = Math.min(lineCount - caretLine - 1, getLineCountWhole()); >+ int scrollOffset; >+ >+ // ensure that scrollLines never gets negative and at leat one >+ // line is scrolled. fixes bug 5602. >+ scrollLines = Math.max(1, scrollLines); >+ caretLine += scrollLines; >+ if (isBidi()) { >+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >+ } >+ else { >+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >+ } >+ if (select) { >+ doSelection(SWT.RIGHT); >+ } >+ // scroll one page down or to the bottom >+ scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement(); >+ if (scrollOffset + pageSize > verticalMaximum) { >+ scrollOffset = verticalMaximum - pageSize; >+ } >+ if (scrollOffset > verticalScrollOffset) { >+ setVerticalScrollOffset(scrollOffset, true); >+ } >+ } >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ // save the original horizontal caret position >+ columnX = oldColumnX; > } > /** > * Moves the cursor to the end of the last fully visible line. > */ > void doPageEnd() { >- // go to end of line if in single line mode. fixes 5673 >- if (isSingleLine()) { >- doLineEnd(); >- } >- else { >- int line = getBottomIndex(); >- int bottomCaretOffset = content.getOffsetAtLine(line) + content.getLine(line).length(); >- >- if (caretOffset < bottomCaretOffset) { >- caretOffset = bottomCaretOffset; >- showCaret(); >- } >- } >+ // go to end of line if in single line mode. fixes 5673 >+ if (isSingleLine()) { >+ doLineEnd(); >+ } >+ else { >+ int line = getBottomIndex(); >+ int bottomCaretOffset = content.getOffsetAtLine(line) + content.getLine(line).length(); >+ >+ if (caretOffset < bottomCaretOffset) { >+ caretOffset = bottomCaretOffset; >+ showCaret(); >+ } >+ } > } > /** > * Moves the cursor to the beginning of the first fully visible line. > */ > void doPageStart() { >- int topCaretOffset = content.getOffsetAtLine(topIndex); >- >- if (caretOffset > topCaretOffset) { >- caretOffset = topCaretOffset; >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(topIndex); >- } >+ int topCaretOffset = content.getOffsetAtLine(topIndex); >+ >+ if (caretOffset > topCaretOffset) { >+ caretOffset = topCaretOffset; >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(topIndex); >+ } > } > /** > * Scrolls one page up so that the first line (truncated or whole) > * of the current page becomes the fully visible last line. >- * The caret is scrolled the same number of lines so that its location >- * relative to the top line remains the same. The exception is the beginning >+ * The caret is scrolled the same number of lines so that its location >+ * relative to the top line remains the same. The exception is the beginning > * of the text where a full page scroll is not possible. In this case the > * caret is moved in front of the first character. > */ > void doPageUp() { >- int oldColumnX = columnX; >- int caretLine = getCaretLine(); >- >- if (caretLine > 0) { >- int scrollLines = Math.max(1, Math.min(caretLine, getLineCountWhole())); >- int scrollOffset; >- >- caretLine -= scrollLines; >- if (isBidi()) { >- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >- } >- else { >- caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >- } >- // scroll one page up or to the top >- scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement()); >- if (scrollOffset < verticalScrollOffset) { >- setVerticalScrollOffset(scrollOffset, true); >- } >- } >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- // save the original horizontal caret position >- columnX = oldColumnX; >+ int oldColumnX = columnX; >+ int caretLine = getCaretLine(); >+ >+ if (caretLine > 0) { >+ int scrollLines = Math.max(1, Math.min(caretLine, getLineCountWhole())); >+ int scrollOffset; >+ >+ caretLine -= scrollLines; >+ if (isBidi()) { >+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine); >+ } >+ else { >+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine); >+ } >+ // scroll one page up or to the top >+ scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement()); >+ if (scrollOffset < verticalScrollOffset) { >+ setVerticalScrollOffset(scrollOffset, true); >+ } >+ } >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ // save the original horizontal caret position >+ columnX = oldColumnX; > } > /** > * Updates the selection to extend to the current caret position. > */ > void doSelection(int direction) { >- int redrawStart = -1; >- int redrawEnd = -1; >- >- if (selectionAnchor == -1) { >- selectionAnchor = selection.x; >- } >- if (direction == SWT.LEFT) { >- if (caretOffset < selection.x) { >- // grow selection >- redrawEnd = selection.x; >- redrawStart = selection.x = caretOffset; >- // check if selection has reversed direction >- if (selection.y != selectionAnchor) { >- redrawEnd = selection.y; >- selection.y = selectionAnchor; >- } >- } >- else // test whether selection actually changed. Fixes 1G71EO1 >- if (selectionAnchor == selection.x && caretOffset < selection.y) { >- // caret moved towards selection anchor (left side of selection). >- // shrink selection >- redrawEnd = selection.y; >- redrawStart = selection.y = caretOffset; >- } >- } >- else { >- if (caretOffset > selection.y) { >- // grow selection >- redrawStart = selection.y; >- redrawEnd = selection.y = caretOffset; >- // check if selection has reversed direction >- if (selection.x != selectionAnchor) { >- redrawStart = selection.x; >- selection.x = selectionAnchor; >- } >- } >- else // test whether selection actually changed. Fixes 1G71EO1 >- if (selectionAnchor == selection.y && caretOffset > selection.x) { >- // caret moved towards selection anchor (right side of selection). >- // shrink selection >- redrawStart = selection.x; >- redrawEnd = selection.x = caretOffset; >- } >- } >- if (redrawStart != -1 && redrawEnd != -1) { >- internalRedrawRange(redrawStart, redrawEnd - redrawStart, true); >- sendSelectionEvent(); >- } >+ int redrawStart = -1; >+ int redrawEnd = -1; >+ >+ if (selectionAnchor == -1) { >+ selectionAnchor = selection.x; >+ } >+ if (direction == SWT.LEFT) { >+ if (caretOffset < selection.x) { >+ // grow selection >+ redrawEnd = selection.x; >+ redrawStart = selection.x = caretOffset; >+ // check if selection has reversed direction >+ if (selection.y != selectionAnchor) { >+ redrawEnd = selection.y; >+ selection.y = selectionAnchor; >+ } >+ } >+ else // test whether selection actually changed. Fixes 1G71EO1 >+ if (selectionAnchor == selection.x && caretOffset < selection.y) { >+ // caret moved towards selection anchor (left side of selection). >+ // shrink selection >+ redrawEnd = selection.y; >+ redrawStart = selection.y = caretOffset; >+ } >+ } >+ else { >+ if (caretOffset > selection.y) { >+ // grow selection >+ redrawStart = selection.y; >+ redrawEnd = selection.y = caretOffset; >+ // check if selection has reversed direction >+ if (selection.x != selectionAnchor) { >+ redrawStart = selection.x; >+ selection.x = selectionAnchor; >+ } >+ } >+ else // test whether selection actually changed. Fixes 1G71EO1 >+ if (selectionAnchor == selection.y && caretOffset > selection.x) { >+ // caret moved towards selection anchor (right side of selection). >+ // shrink selection >+ redrawStart = selection.x; >+ redrawEnd = selection.x = caretOffset; >+ } >+ } >+ if (redrawStart != -1 && redrawEnd != -1) { >+ internalRedrawRange(redrawStart, redrawEnd - redrawStart, true); >+ sendSelectionEvent(); >+ } > } > /** >- * Moves the caret to the next character or to the beginning of the >+ * Moves the caret to the next character or to the beginning of the > * next line if the cursor is at the end of a line. > */ > void doSelectionCursorNext() { >- int caretLine = getCaretLine(); >- int lineOffset = content.getOffsetAtLine(caretLine); >- int offsetInLine = caretOffset - lineOffset; >- >- if (offsetInLine < content.getLine(caretLine).length()) { >- // Remember the last direction. Always update lastCaretDirection, >- // even though it's not used in non-bidi mode in order to avoid >- // extra methods. >- lastCaretDirection = ST.COLUMN_NEXT; >- caretOffset++; >- showCaret(); >- } >- else >- if (caretLine < content.getLineCount() - 1 && isSingleLine() == false) { >- // only go to next line if not in single line mode. fixes 5673 >- caretLine++; >- caretOffset = content.getOffsetAtLine(caretLine); >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- } >+ int caretLine = getCaretLine(); >+ int lineOffset = content.getOffsetAtLine(caretLine); >+ int offsetInLine = caretOffset - lineOffset; >+ >+ if (offsetInLine < content.getLine(caretLine).length()) { >+ // Remember the last direction. Always update lastCaretDirection, >+ // even though it's not used in non-bidi mode in order to avoid >+ // extra methods. >+ lastCaretDirection = ST.COLUMN_NEXT; >+ caretOffset++; >+ showCaret(); >+ } >+ else >+ if (caretLine < content.getLineCount() - 1 && isSingleLine() == false) { >+ // only go to next line if not in single line mode. fixes 5673 >+ caretLine++; >+ caretOffset = content.getOffsetAtLine(caretLine); >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ } > } > /** >- * Moves the caret to the previous character or to the end of the previous >+ * Moves the caret to the previous character or to the end of the previous > * line if the cursor is at the beginning of a line. > */ > void doSelectionCursorPrevious() { >- int caretLine = getCaretLine(); >- int lineOffset = content.getOffsetAtLine(caretLine); >- int offsetInLine = caretOffset - lineOffset; >- >- if (offsetInLine > 0) { >- // Remember the last direction. Always update lastCaretDirection, >- // even though it's not used in non-bidi mode in order to avoid >- // extra methods. >- lastCaretDirection = ST.COLUMN_PREVIOUS; >- caretOffset--; >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- } >- else >- if (caretLine > 0) { >- caretLine--; >- lineOffset = content.getOffsetAtLine(caretLine); >- caretOffset = lineOffset + content.getLine(caretLine).length(); >- showCaret(); >- } >+ int caretLine = getCaretLine(); >+ int lineOffset = content.getOffsetAtLine(caretLine); >+ int offsetInLine = caretOffset - lineOffset; >+ >+ if (offsetInLine > 0) { >+ // Remember the last direction. Always update lastCaretDirection, >+ // even though it's not used in non-bidi mode in order to avoid >+ // extra methods. >+ lastCaretDirection = ST.COLUMN_PREVIOUS; >+ caretOffset--; >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ } >+ else >+ if (caretLine > 0) { >+ caretLine--; >+ lineOffset = content.getOffsetAtLine(caretLine); >+ caretOffset = lineOffset + content.getLine(caretLine).length(); >+ showCaret(); >+ } > } > /** >- * Moves the caret one line down and to the same character offset relative >- * to the beginning of the line. Moves the caret to the end of the new line >+ * Moves the caret one line down and to the same character offset relative >+ * to the beginning of the line. Moves the caret to the end of the new line > * if the new line is shorter than the character offset. >- * Moves the caret to the end of the text if the caret already is on the >+ * Moves the caret to the end of the text if the caret already is on the > * last line. > * Adjusts the selection according to the caret change. This can either add > * to or subtract from the old selection, depending on the previous selection > * direction. > */ > void doSelectionLineDown() { >- int oldColumnX = columnX; >- int caretLine; >- >- if (isSingleLine()) { >- return; >- } >- caretLine = getCaretLine(); >- if (caretLine == content.getLineCount() - 1) { >- caretOffset = content.getCharCount(); >- } >- else { >- caretLine = doLineDown(); >- } >- setMouseWordSelectionAnchor(); >- // select first and then scroll to reduce flash when key >- // repeat scrolls lots of lines >- doSelection(SWT.RIGHT); >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- // save the original horizontal caret position >- columnX = oldColumnX; >+ int oldColumnX = columnX; >+ int caretLine; >+ >+ if (isSingleLine()) { >+ return; >+ } >+ caretLine = getCaretLine(); >+ if (caretLine == content.getLineCount() - 1) { >+ caretOffset = content.getCharCount(); >+ } >+ else { >+ caretLine = doLineDown(); >+ } >+ setMouseWordSelectionAnchor(); >+ // select first and then scroll to reduce flash when key >+ // repeat scrolls lots of lines >+ doSelection(SWT.RIGHT); >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ // save the original horizontal caret position >+ columnX = oldColumnX; > } > /** >- * Moves the caret one line up and to the same character offset relative >- * to the beginning of the line. Moves the caret to the end of the new line >+ * Moves the caret one line up and to the same character offset relative >+ * to the beginning of the line. Moves the caret to the end of the new line > * if the new line is shorter than the character offset. > * Moves the caret to the beginning of the document if it is already on the > * first line. >@@ -3251,54 +3261,54 @@ > * direction. > */ > void doSelectionLineUp() { >- int oldColumnX = columnX; >- int caretLine = getCaretLine(); >- >- if (caretLine == 0) { >- caretOffset = 0; >- } >- else { >- caretLine = doLineUp(); >- } >- setMouseWordSelectionAnchor(); >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- doSelection(SWT.LEFT); >- // save the original horizontal caret position >- columnX = oldColumnX; >+ int oldColumnX = columnX; >+ int caretLine = getCaretLine(); >+ >+ if (caretLine == 0) { >+ caretOffset = 0; >+ } >+ else { >+ caretLine = doLineUp(); >+ } >+ setMouseWordSelectionAnchor(); >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ doSelection(SWT.LEFT); >+ // save the original horizontal caret position >+ columnX = oldColumnX; > } > /** > * Moves the caret to the end of the next word . > */ > void doSelectionWordNext() { >- int newCaretOffset = getWordEnd(caretOffset); >- >- // don't change caret position if in single line mode and the cursor >- // would be on a different line. fixes 5673 >- if (isSingleLine() == false || >- content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) { >- lastCaretDirection = ST.COLUMN_NEXT; >- caretOffset = newCaretOffset; >- showCaret(); >- } >+ int newCaretOffset = getWordEnd(caretOffset); >+ >+ // don't change caret position if in single line mode and the cursor >+ // would be on a different line. fixes 5673 >+ if (isSingleLine() == false || >+ content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) { >+ lastCaretDirection = ST.COLUMN_NEXT; >+ caretOffset = newCaretOffset; >+ showCaret(); >+ } > } > /** > * Moves the caret to the start of the previous word. > */ > void doSelectionWordPrevious() { >- int caretLine; >- >- lastCaretDirection = ST.COLUMN_PREVIOUS; >- caretOffset = getWordStart(caretOffset); >- caretLine = content.getLineAtOffset(caretOffset); >- // word previous always comes from bottom line. when >- // wrapping lines, stay on bottom line when on line boundary >- if (wordWrap && caretLine < content.getLineCount() - 1 && >- caretOffset == content.getOffsetAtLine(caretLine + 1)) { >- caretLine++; >- } >- showCaret(caretLine); >+ int caretLine; >+ >+ lastCaretDirection = ST.COLUMN_PREVIOUS; >+ caretOffset = getWordStart(caretOffset); >+ caretLine = content.getLineAtOffset(caretOffset); >+ // word previous always comes from bottom line. when >+ // wrapping lines, stay on bottom line when on line boundary >+ if (wordWrap && caretLine < content.getLineCount() - 1 && >+ caretOffset == content.getOffsetAtLine(caretLine + 1)) { >+ caretLine++; >+ } >+ showCaret(caretLine); > } > /** > * Moves the caret to the end of the next word. >@@ -3306,16 +3316,16 @@ > * and remove the selection. > */ > void doWordNext() { >- if (selection.y - selection.x > 0) { >- int caretLine; >- >- caretOffset = selection.y; >- caretLine = getCaretLine(); >- showCaret(caretLine); >- } >- else { >- doSelectionWordNext(); >- } >+ if (selection.y - selection.x > 0) { >+ int caretLine; >+ >+ caretOffset = selection.y; >+ caretLine = getCaretLine(); >+ showCaret(caretLine); >+ } >+ else { >+ doSelectionWordNext(); >+ } > } > /** > * Moves the caret to the start of the previous word. >@@ -3323,20 +3333,20 @@ > * and remove the selection. > */ > void doWordPrevious() { >- if (selection.y - selection.x > 0) { >- int caretLine; >- >- caretOffset = selection.x; >- caretLine = getCaretLine(); >- showCaret(caretLine); >- } >- else { >- doSelectionWordPrevious(); >- } >+ if (selection.y - selection.x > 0) { >+ int caretLine; >+ >+ caretOffset = selection.x; >+ caretLine = getCaretLine(); >+ showCaret(caretLine); >+ } >+ else { >+ doSelectionWordPrevious(); >+ } > } > /** > * Draws the specified rectangle. >- * Draw directly without invalidating the affected area when clearBackground is >+ * Draw directly without invalidating the affected area when clearBackground is > * false. > * <p> > * >@@ -3344,53 +3354,53 @@ > * @param y the y position > * @param width the width > * @param height the height >- * @param clearBackground true=clear the background by invalidating the requested >- * redraw area, false=draw the foreground directly without invalidating the >- * redraw area. >+ * @param clearBackground true=clear the background by invalidating the requested >+ * redraw area, false=draw the foreground directly without invalidating the >+ * redraw area. > */ > void draw(int x, int y, int width, int height, boolean clearBackground) { >- if (clearBackground) { >- redraw(x + leftMargin, y + topMargin, width, height, true); >- } >- else { >- int startLine = (y + verticalScrollOffset) / lineHeight; >- int endY = y + height; >- int paintYFromTopLine = (startLine - topIndex) * lineHeight; >- int topLineOffset = (topIndex * lineHeight - verticalScrollOffset); >- int paintY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling >- int lineCount = content.getLineCount(); >- Color background = getBackground(); >- Color foreground = getForeground(); >- GC gc = getGC(); >- >- if (isSingleLine()) { >- lineCount = 1; >- if (startLine > 1) { >- startLine = 1; >- } >- } >- for (int i = startLine; paintY < endY && i < lineCount; i++, paintY += lineHeight) { >- String line = content.getLine(i); >- renderer.drawLine(line, i, paintY, gc, background, foreground, clearBackground); >- } >- gc.dispose(); >- } >+ if (clearBackground) { >+ redraw(x + leftMargin, y + topMargin, width, height, true); >+ } >+ else { >+ int startLine = (y + verticalScrollOffset) / lineHeight; >+ int endY = y + height; >+ int paintYFromTopLine = (startLine - topIndex) * lineHeight; >+ int topLineOffset = (topIndex * lineHeight - verticalScrollOffset); >+ int paintY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling >+ int lineCount = content.getLineCount(); >+ Color background = getBackground(); >+ Color foreground = getForeground(); >+ GC gc = getGC(); >+ >+ if (isSingleLine()) { >+ lineCount = 1; >+ if (startLine > 1) { >+ startLine = 1; >+ } >+ } >+ for (int i = startLine; paintY < endY && i < lineCount; i++, paintY += lineHeight) { >+ String line = content.getLine(i); >+ renderer.drawLine(line, i, paintY, gc, background, foreground, clearBackground); >+ } >+ gc.dispose(); >+ } > } >-/** >+/** > * Ends the autoscroll process. > */ > void endAutoScroll() { >- autoScrollDirection = SWT.NULL; >+ autoScrollDirection = SWT.NULL; > } > /** > * @see org.eclipse.swt.widgets.Control#getBackground > */ > public Color getBackground() { >- checkWidget(); >- if (background == null) { >- return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND); >- } >- return background; >+ checkWidget(); >+ if (background == null) { >+ return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND); >+ } >+ return background; > } > /** > * Gets the BIDI coloring mode. When true the BIDI text display >@@ -3407,70 +3417,70 @@ > * </p> > */ > public boolean getBidiColoring() { >- checkWidget(); >- return bidiColoring; >+ checkWidget(); >+ return bidiColoring; > } > /** > * Returns the offset at the specified x location in the specified line. >- * Also sets the caret direction so that the caret is placed correctly >+ * Also sets the caret direction so that the caret is placed correctly > * depending on whether the mouse location is in a R2L or L2R segment. > * <p> > * >- * @param x x location of the mouse location >- * @param line line the mouse location is in >+ * @param x x location of the mouse location >+ * @param line line the mouse location is in > * @return the offset at the specified x location in the specified line, >- * relative to the beginning of the document >+ * relative to the beginning of the document > */ > int getBidiOffsetAtMouseLocation(int x, int line) { >- String lineText = content.getLine(line); >- int lineOffset = content.getOffsetAtLine(line); >- GC gc = getGC(); >- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >- int[] values; >- int offsetInLine; >- x += horizontalScrollOffset; >- values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin); >- offsetInLine = values[0]; >- lastCaretDirection = values[1]; >- gc.dispose(); >- >- return lineOffset + offsetInLine; >+ String lineText = content.getLine(line); >+ int lineOffset = content.getOffsetAtLine(line); >+ GC gc = getGC(); >+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >+ int[] values; >+ int offsetInLine; >+ x += horizontalScrollOffset; >+ values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin); >+ offsetInLine = values[0]; >+ lastCaretDirection = values[1]; >+ gc.dispose(); >+ >+ return lineOffset + offsetInLine; > } > /** >- * Returns the x position of the character at the specified offset >+ * Returns the x position of the character at the specified offset > * relative to the first character in the line. > * </p> > * > * @param text text to be measured. > * @param endOffset offset of the character > * @param bidi the bidi object to use for measuring text in bidi >- * locales. >- * @return x position of the character at the specified offset. >- * 0 if the length is outside the specified text. >+ * locales. >+ * @return x position of the character at the specified offset. >+ * 0 if the length is outside the specified text. > */ > int getBidiTextPosition(String text, int endOffset, StyledTextBidi bidi) { >- if (endOffset > text.length()) { >- return 0; >- } >- // Use lastCaretDirection in order to get same results as during >- // caret positioning (setBidiCaretLocation). Fixes 1GKU4C5. >- return bidi.getTextPosition(endOffset, lastCaretDirection); >+ if (endOffset > text.length()) { >+ return 0; >+ } >+ // Use lastCaretDirection in order to get same results as during >+ // caret positioning (setBidiCaretLocation). Fixes 1GKU4C5. >+ return bidi.getTextPosition(endOffset, lastCaretDirection); > } >-/** >+/** > * Returns the index of the last fully visible line. > * <p> > * > * @return index of the last fully visible line. > */ > int getBottomIndex() { >- int lineCount = 1; >- >- if (lineHeight != 0) { >- // calculate the number of lines that are fully visible >- int partialTopLineHeight = topIndex * lineHeight - verticalScrollOffset; >- lineCount = (getClientArea().height - partialTopLineHeight) / lineHeight; >- } >- return Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1)); >+ int lineCount = 1; >+ >+ if (lineHeight != 0) { >+ // calculate the number of lines that are fully visible >+ int partialTopLineHeight = topIndex * lineHeight - verticalScrollOffset; >+ lineCount = (getClientArea().height - partialTopLineHeight) / lineHeight; >+ } >+ return Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1)); > } > /** > * Returns the caret position relative to the start of the text. >@@ -3483,53 +3493,53 @@ > * </ul> > */ > public int getCaretOffset() { >- checkWidget(); >- >- return caretOffset; >+ checkWidget(); >+ >+ return caretOffset; > } > /** > * Returns the caret offset at the given x location in the line. > * The caret offset is the offset of the character where the caret will be >- * placed when a mouse click occurs. The caret offset will be the offset of >- * the character after the clicked one if the mouse click occurs at the second >+ * placed when a mouse click occurs. The caret offset will be the offset of >+ * the character after the clicked one if the mouse click occurs at the second > * half of a character. >- * Doesn't properly handle ligatures and other context dependent characters >- * unless the current locale is a bidi locale. >+ * Doesn't properly handle ligatures and other context dependent characters >+ * unless the current locale is a bidi locale. > * Ligatures are handled properly as long as they don't occur at lineXOffset. > * <p> > * > * @param line text of the line to calculate the offset in >- * @param lineOffset offset of the first character in the line. >- * 0 based from the beginning of the document. >+ * @param lineOffset offset of the first character in the line. >+ * 0 based from the beginning of the document. > * @param lineXOffset x location in the line > * @return caret offset at the x location relative to the start of the line. > */ > int getCaretOffsetAtX(String line, int lineOffset, int lineXOffset) { >- int offset = 0; >- GC gc = getGC(); >- StyleRange[] styles = null; >- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >- >- lineXOffset += horizontalScrollOffset; >- if (event != null) { >- styles = renderer.filterLineStyles(event.styles); >- } >- int low = -1; >- int high = line.length(); >- while (high - low > 1) { >- offset = (high + low) / 2; >- int x = renderer.getTextPosition(line, lineOffset, offset, styles, gc) + leftMargin; >- int charWidth = renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc) + leftMargin - x; >- if (lineXOffset <= x + charWidth / 2) { >- high = offset; >- } >- else { >- low = offset; >- } >- } >- offset = high; >- gc.dispose(); >- return offset; >+ int offset = 0; >+ GC gc = getGC(); >+ StyleRange[] styles = null; >+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >+ >+ lineXOffset += horizontalScrollOffset; >+ if (event != null) { >+ styles = renderer.filterLineStyles(event.styles); >+ } >+ int low = -1; >+ int high = line.length(); >+ while (high - low > 1) { >+ offset = (high + low) / 2; >+ int x = renderer.getTextPosition(line, lineOffset, offset, styles, gc) + leftMargin; >+ int charWidth = renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc) + leftMargin - x; >+ if (lineXOffset <= x + charWidth / 2) { >+ high = offset; >+ } >+ else { >+ low = offset; >+ } >+ } >+ offset = high; >+ gc.dispose(); >+ return offset; > } > /** > * Returns the caret width. >@@ -3538,16 +3548,16 @@ > * @return the caret width, 0 if caret is null. > */ > int getCaretWidth() { >- Caret caret = getCaret(); >- if (caret == null) return 0; >- return caret.getSize().x; >+ Caret caret = getCaret(); >+ if (caret == null) return 0; >+ return caret.getSize().x; > } > /** > * Returns the content implementation that is used for text storage > * or null if no user defined content implementation has been set. > * <p> > * >- * @return content implementation that is used for text storage or null >+ * @return content implementation that is used for text storage or null > * if no user defined content implementation has been set. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -3555,11 +3565,11 @@ > * </ul> > */ > public StyledTextContent getContent() { >- checkWidget(); >- >- return logicalContent; >+ checkWidget(); >+ >+ return logicalContent; > } >-/** >+/** > * Returns whether the widget implements double click mouse behavior. > * <p> > * >@@ -3571,8 +3581,8 @@ > * </ul> > */ > public boolean getDoubleClickEnabled() { >- checkWidget(); >- return doubleClickEnabled; >+ checkWidget(); >+ return doubleClickEnabled; > } > /** > * Returns whether the widget content can be edited. >@@ -3585,20 +3595,20 @@ > * </ul> > */ > public boolean getEditable() { >- checkWidget(); >- return editable; >+ checkWidget(); >+ return editable; > } > /** > * @see org.eclipse.swt.widgets.Control#getForeground > */ > public Color getForeground() { >- checkWidget(); >- if (foreground == null) { >- return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND); >- } >- return foreground; >+ checkWidget(); >+ if (foreground == null) { >+ return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND); >+ } >+ return foreground; > } >-/** >+/** > * Return a GC to use for rendering and update the cached font style to > * represent the current style. > * <p> >@@ -3606,23 +3616,23 @@ > * @return GC. > */ > GC getGC() { >- renderer.setCurrentFontStyle(SWT.NORMAL); >- return new GC(this); >+ renderer.setCurrentFontStyle(SWT.NORMAL); >+ return new GC(this); > } >-/** >+/** > * Returns the horizontal scroll increment. > * <p> > * > * @return horizontal scroll increment. > */ > int getHorizontalIncrement() { >- GC gc = getGC(); >- int increment = gc.getFontMetrics().getAverageCharWidth(); >- >- gc.dispose(); >- return increment; >+ GC gc = getGC(); >+ int increment = gc.getFontMetrics().getAverageCharWidth(); >+ >+ gc.dispose(); >+ return increment; > } >-/** >+/** > * Returns the horizontal scroll offset relative to the start of the line. > * <p> > * >@@ -3633,11 +3643,11 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ >-public int getHorizontalIndex() { >- checkWidget(); >- return horizontalScrollOffset / getHorizontalIncrement(); >+public int getHorizontalIndex() { >+ checkWidget(); >+ return horizontalScrollOffset / getHorizontalIncrement(); > } >-/** >+/** > * Returns the horizontal scroll offset relative to the start of the line. > * <p> > * >@@ -3648,39 +3658,39 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ >-public int getHorizontalPixel() { >- checkWidget(); >- return horizontalScrollOffset; >+public int getHorizontalPixel() { >+ checkWidget(); >+ return horizontalScrollOffset; > } >-/** >+/** > * Returns the action assigned to the key. > * Returns SWT.NULL if there is no action associated with the key. > * <p> > * >- * @param key a key code defined in SWT.java or a character. >- * Optionally ORd with a state mask. Preferred state masks are one or more of >- * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform >+ * @param key a key code defined in SWT.java or a character. >+ * Optionally ORd with a state mask. Preferred state masks are one or more of >+ * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform > * differences. However, there may be cases where using the specific state masks > * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense. >- * @return one of the predefined actions defined in ST.java or SWT.NULL >- * if there is no action associated with the key. >+ * @return one of the predefined actions defined in ST.java or SWT.NULL >+ * if there is no action associated with the key. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > public int getKeyBinding(int key) { >- checkWidget(); >- Integer action = (Integer) keyActionMap.get(new Integer(key)); >- int intAction; >- >- if (action == null) { >- intAction = SWT.NULL; >- } >- else { >- intAction = action.intValue(); >- } >- return intAction; >+ checkWidget(); >+ Integer action = (Integer) keyActionMap.get(new Integer(key)); >+ int intAction; >+ >+ if (action == null) { >+ intAction = SWT.NULL; >+ } >+ else { >+ intAction = action.intValue(); >+ } >+ return intAction; > } > /** > * Gets the number of characters. >@@ -3693,12 +3703,12 @@ > * </ul> > */ > public int getCharCount() { >- checkWidget(); >- return content.getCharCount(); >+ checkWidget(); >+ return content.getCharCount(); > } > /** > * Returns the background color of the line at the given index. >- * Returns null if a LineBackgroundListener has been set or if no background >+ * Returns null if a LineBackgroundListener has been set or if no background > * color has been specified for the line. Should not be called if a > * LineBackgroundListener has been set since the listener maintains the > * line background colors. >@@ -3714,30 +3724,30 @@ > * </ul> > */ > public Color getLineBackground(int index) { >- checkWidget(); >- Color lineBackground = null; >- >- if (index < 0 || index > logicalContent.getLineCount()) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- if (userLineBackground == false) { >- lineBackground = defaultLineStyler.getLineBackground(index); >- } >- return lineBackground; >+ checkWidget(); >+ Color lineBackground = null; >+ >+ if (index < 0 || index > logicalContent.getLineCount()) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ if (userLineBackground == false) { >+ lineBackground = defaultLineStyler.getLineBackground(index); >+ } >+ return lineBackground; > } > /** >- * Returns the line background data for the given line or null if >+ * Returns the line background data for the given line or null if > * there is none. > * <p> > * @param lineOffset offset of the line start relative to the start >- * of the content. >+ * of the content. > * @param line line to get line background data for > * @return line background data for the given line. > */ > StyledTextEvent getLineBackgroundData(int lineOffset, String line) { >- return sendLineEvent(LineGetBackground, lineOffset, line); >+ return sendLineEvent(LineGetBackground, lineOffset, line); > } >-/** >+/** > * Gets the number of text lines. > * <p> > * >@@ -3748,27 +3758,27 @@ > * </ul> > */ > public int getLineCount() { >- checkWidget(); >- return getLineAtOffset(getCharCount()) + 1; >+ checkWidget(); >+ return getLineAtOffset(getCharCount()) + 1; > } > /** >- * Returns the number of lines that can be completely displayed in the >+ * Returns the number of lines that can be completely displayed in the > * widget client area. > * <p> > * >- * @return number of lines that can be completely displayed in the widget >- * client area. >+ * @return number of lines that can be completely displayed in the widget >+ * client area. > */ > int getLineCountWhole() { >- int lineCount; >- >- if (lineHeight != 0) { >- lineCount = getClientArea().height / lineHeight; >- } >- else { >- lineCount = 1; >- } >- return lineCount; >+ int lineCount; >+ >+ if (lineHeight != 0) { >+ lineCount = getClientArea().height / lineHeight; >+ } >+ else { >+ lineCount = 1; >+ } >+ return lineCount; > } > /** > * Returns the line at the specified offset in the text. >@@ -3776,24 +3786,24 @@ > * returns the line of the insert location. > * <p> > * >- * @param offset offset relative to the start of the content. >- * 0 <= offset <= getCharCount() >+ * @param offset offset relative to the start of the content. >+ * 0 <= offset <= getCharCount() > * @return line at the specified offset in the text > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> >+ * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> > * </ul> > */ > public int getLineAtOffset(int offset) { >- checkWidget(); >- >- if (offset < 0 || offset > getCharCount()) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- return logicalContent.getLineAtOffset(offset); >+ checkWidget(); >+ >+ if (offset < 0 || offset > getCharCount()) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ return logicalContent.getLineAtOffset(offset); > } > /** > * Returns the line delimiter used for entering new lines by key down >@@ -3808,43 +3818,43 @@ > * </ul> > */ > public String getLineDelimiter() { >- checkWidget(); >- return content.getLineDelimiter(); >+ checkWidget(); >+ return content.getLineDelimiter(); > } > /** >- * Returns a StyledTextEvent that can be used to request data such >+ * Returns a StyledTextEvent that can be used to request data such > * as styles and background color for a line. >- * The specified line may be a visual (wrapped) line if in word >- * wrap mode. The returned object will always be for a logical >+ * The specified line may be a visual (wrapped) line if in word >+ * wrap mode. The returned object will always be for a logical > * (unwrapped) line. > * <p> > * > * @param lineOffset offset of the line. This may be the offset of >- * a visual line if the widget is in word wrap mode. >- * @param line line text. This may be the text of a visualline if >- * the widget is in word wrap mode. >- * @return StyledTextEvent that can be used to request line data >- * for the given line. >+ * a visual line if the widget is in word wrap mode. >+ * @param line line text. This may be the text of a visualline if >+ * the widget is in word wrap mode. >+ * @return StyledTextEvent that can be used to request line data >+ * for the given line. > */ > StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) { >- StyledTextEvent event = null; >- >- if (isListening(eventType)) { >- event = new StyledTextEvent(logicalContent); >- if (wordWrap) { >- // if word wrap is on, the line offset and text may be visual (wrapped) >- int lineIndex = logicalContent.getLineAtOffset(lineOffset); >- >- event.detail = logicalContent.getOffsetAtLine(lineIndex); >- event.text = logicalContent.getLine(lineIndex); >- } >- else { >- event.detail = lineOffset; >- event.text = line; >- } >- notifyListeners(eventType, event); >- } >- return event; >+ StyledTextEvent event = null; >+ >+ if (isListening(eventType)) { >+ event = new StyledTextEvent(logicalContent); >+ if (wordWrap) { >+ // if word wrap is on, the line offset and text may be visual (wrapped) >+ int lineIndex = logicalContent.getLineAtOffset(lineOffset); >+ >+ event.detail = logicalContent.getOffsetAtLine(lineIndex); >+ event.text = logicalContent.getLine(lineIndex); >+ } >+ else { >+ event.detail = lineOffset; >+ event.text = line; >+ } >+ notifyListeners(eventType, event); >+ } >+ return event; > } > /** > * Returns the line height. >@@ -3857,108 +3867,108 @@ > * </ul> > */ > public int getLineHeight() { >- checkWidget(); >- return lineHeight; >+ checkWidget(); >+ return lineHeight; > } > /** > * Returns a LineCache implementation. Depending on whether or not >- * word wrap is on this may be a line wrapping or line width >+ * word wrap is on this may be a line wrapping or line width > * calculating implementaiton. > * <p> >- * >+ * > * @param content StyledTextContent to create the LineCache on. > * @return a LineCache implementation > */ > LineCache getLineCache(StyledTextContent content) { > LineCache lineCache; >- >- if (wordWrap) { >- lineCache = new WordWrapCache(this, (WrappedContent) content); >- } >- else { >- lineCache = new ContentWidthCache(this, content.getLineCount()); >- } >- return lineCache; >+ >+ if (wordWrap) { >+ lineCache = new WordWrapCache(this, (WrappedContent) content); >+ } >+ else { >+ lineCache = new ContentWidthCache(this, content.getLineCount()); >+ } >+ return lineCache; > } > /** >- * Returns the line style data for the given line or null if there is >- * none. If there is a LineStyleListener but it does not set any styles, >- * the StyledTextEvent.styles field will be initialized to an empty >+ * Returns the line style data for the given line or null if there is >+ * none. If there is a LineStyleListener but it does not set any styles, >+ * the StyledTextEvent.styles field will be initialized to an empty > * array. > * <p> >- * >- * @param lineOffset offset of the line start relative to the start of >- * the content. >+ * >+ * @param lineOffset offset of the line start relative to the start of >+ * the content. > * @param line line to get line styles for >- * @return line style data for the given line. Styles may start before >- * line start and end after line end >+ * @return line style data for the given line. Styles may start before >+ * line start and end after line end > */ > StyledTextEvent getLineStyleData(int lineOffset, String line) { >- return sendLineEvent(LineGetStyle, lineOffset, line); >+ return sendLineEvent(LineGetStyle, lineOffset, line); > } > /** >- * Returns the x, y location of the upper left corner of the character >- * bounding box at the specified offset in the text. The point is >+ * Returns the x, y location of the upper left corner of the character >+ * bounding box at the specified offset in the text. The point is > * relative to the upper left corner of the widget client area. > * <p> > * >- * @param offset offset relative to the start of the content. >- * 0 <= offset <= getCharCount() >- * @return x, y location of the upper left corner of the character >- * bounding box at the specified offset in the text. >+ * @param offset offset relative to the start of the content. >+ * 0 <= offset <= getCharCount() >+ * @return x, y location of the upper left corner of the character >+ * bounding box at the specified offset in the text. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> >+ * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> > * </ul> > */ > public Point getLocationAtOffset(int offset) { >- checkWidget(); >- if (offset < 0 || offset > getCharCount()) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- int line = content.getLineAtOffset(offset); >- int lineOffset = content.getOffsetAtLine(line); >- String lineContent = content.getLine(line); >- int x = getXAtOffset(lineContent, line, offset - lineOffset); >- int y = line * lineHeight - verticalScrollOffset; >- >- return new Point(x, y); >+ checkWidget(); >+ if (offset < 0 || offset > getCharCount()) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ int line = content.getLineAtOffset(offset); >+ int lineOffset = content.getOffsetAtLine(line); >+ String lineContent = content.getLine(line); >+ int x = getXAtOffset(lineContent, line, offset - lineOffset); >+ int y = line * lineHeight - verticalScrollOffset; >+ >+ return new Point(x, y); > } > /** > * Returns the character offset of the first character of the given line. > * <p> > * >- * @param lineIndex index of the line, 0 based relative to the first >- * line in the content. 0 <= lineIndex < getLineCount(), except >- * lineIndex may always be 0 >+ * @param lineIndex index of the line, 0 based relative to the first >+ * line in the content. 0 <= lineIndex < getLineCount(), except >+ * lineIndex may always be 0 > * @return offset offset of the first character of the line, relative to >- * the beginning of the document. The first character of the document is >- * at offset 0. >- * When there are not any lines, getOffsetAtLine(0) is a valid call that >- * answers 0. >+ * the beginning of the document. The first character of the document is >+ * at offset 0. >+ * When there are not any lines, getOffsetAtLine(0) is a valid call that >+ * answers 0. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> >+ * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> > * </ul> > * @since 2.0 > */ > public int getOffsetAtLine(int lineIndex) { >- checkWidget(); >- >- if (lineIndex < 0 || >- (lineIndex > 0 && lineIndex >= logicalContent.getLineCount())) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- return logicalContent.getOffsetAtLine(lineIndex); >+ checkWidget(); >+ >+ if (lineIndex < 0 || >+ (lineIndex > 0 && lineIndex >= logicalContent.getLineCount())) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ return logicalContent.getOffsetAtLine(lineIndex); > } > /** >- * Returns the offset of the character at the given location relative >+ * Returns the offset of the character at the given location relative > * to the first character in the document. > * The return value reflects the character offset that the caret will > * be placed at if a mouse click occurred at the specified location. >@@ -3966,10 +3976,10 @@ > * the returned offset will be behind the character. > * <p> > * >- * @param point the origin of character bounding box relative to >- * the origin of the widget client area. >- * @return offset of the character at the given location relative >- * to the first character in the document. >+ * @param point the origin of character bounding box relative to >+ * the origin of the widget client area. >+ * @return offset of the character at the given location relative >+ * to the first character in the document. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> >@@ -3980,148 +3990,148 @@ > * </ul> > */ > public int getOffsetAtLocation(Point point) { >- checkWidget(); >- int line; >- int lineOffset; >- int offsetInLine; >- String lineText; >- >- if (point == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- // is y above first line or is x before first column? >- if (point.y + verticalScrollOffset < 0 || point.x + horizontalScrollOffset < 0) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- line = (getTopPixel() + point.y) / lineHeight; >- // does the referenced line exist? >- if (line >= content.getLineCount()) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- lineText = content.getLine(line); >- lineOffset = content.getOffsetAtLine(line); >- offsetInLine = getOffsetAtX(lineText, lineOffset, point.x); >- // is the x position within the line? >- if (offsetInLine == -1) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- return lineOffset + offsetInLine; >+ checkWidget(); >+ int line; >+ int lineOffset; >+ int offsetInLine; >+ String lineText; >+ >+ if (point == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ // is y above first line or is x before first column? >+ if (point.y + verticalScrollOffset < 0 || point.x + horizontalScrollOffset < 0) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ line = (getTopPixel() + point.y) / lineHeight; >+ // does the referenced line exist? >+ if (line >= content.getLineCount()) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ lineText = content.getLine(line); >+ lineOffset = content.getOffsetAtLine(line); >+ offsetInLine = getOffsetAtX(lineText, lineOffset, point.x); >+ // is the x position within the line? >+ if (offsetInLine == -1) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ return lineOffset + offsetInLine; > } > /** > * Returns the offset at the specified x location in the specified line. > * <p> > * >- * @param x x location of the mouse location >- * @param line line the mouse location is in >+ * @param x x location of the mouse location >+ * @param line line the mouse location is in > * @return the offset at the specified x location in the specified line, >- * relative to the beginning of the document >+ * relative to the beginning of the document > */ > int getOffsetAtMouseLocation(int x, int line) { >- String lineText = content.getLine(line); >- int lineOffset = content.getOffsetAtLine(line); >- int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x); >- return lineOffset + offsetInLine; >+ String lineText = content.getLine(line); >+ int lineOffset = content.getOffsetAtLine(line); >+ int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x); >+ return lineOffset + offsetInLine; > } > /** > * Returns the offset of the character at the given x location in the line. > * <p> > * > * @param line text of the line to calculate the offset in >- * @param lineOffset offset of the first character in the line. >- * 0 based from the beginning of the document. >+ * @param lineOffset offset of the first character in the line. >+ * 0 based from the beginning of the document. > * @param lineXOffset x location in the line >- * @return offset of the character at the x location relative to the start >- * of the line. -1 if the x location is past the end if the line. >+ * @return offset of the character at the x location relative to the start >+ * of the line. -1 if the x location is past the end if the line. > */ > int getOffsetAtX(String line, int lineOffset, int lineXOffset) { >- GC gc = getGC(); >- int offset; >- >- lineXOffset += (horizontalScrollOffset - leftMargin); >- if (isBidi()) { >- StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc); >- offset = bidi.getOffsetAtX(lineXOffset); >- } >- else { >- StyleRange[] styles = null; >- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >- >- if (event != null) { >- styles = renderer.filterLineStyles(event.styles); >- } >- int low = -1; >- int high = line.length(); >- while (high - low > 1) { >- offset = (high + low) / 2; >- // Restrict right/high search boundary only if x is within searched text segment. >- // Fixes 1GL4ZVE. >- if (lineXOffset < renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc)) { >- high = offset; >- } >- else >- if (high == line.length() && high - offset == 1) { >- // requested x location is past end of line >- high = -1; >- } >- else { >- low = offset; >- } >- } >- offset = high; >- } >- gc.dispose(); >- return offset; >+ GC gc = getGC(); >+ int offset; >+ >+ lineXOffset += (horizontalScrollOffset - leftMargin); >+ if (isBidi()) { >+ StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc); >+ offset = bidi.getOffsetAtX(lineXOffset); >+ } >+ else { >+ StyleRange[] styles = null; >+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >+ >+ if (event != null) { >+ styles = renderer.filterLineStyles(event.styles); >+ } >+ int low = -1; >+ int high = line.length(); >+ while (high - low > 1) { >+ offset = (high + low) / 2; >+ // Restrict right/high search boundary only if x is within searched text segment. >+ // Fixes 1GL4ZVE. >+ if (lineXOffset < renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc)) { >+ high = offset; >+ } >+ else >+ if (high == line.length() && high - offset == 1) { >+ // requested x location is past end of line >+ high = -1; >+ } >+ else { >+ low = offset; >+ } >+ } >+ offset = high; >+ } >+ gc.dispose(); >+ return offset; > } >-/** >+/** > * Returns the index of the last partially visible line. > * > * @return index of the last partially visible line. > */ > int getPartialBottomIndex() { >- int partialLineCount = Compatibility.ceil(getClientArea().height, lineHeight); >- return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1; >+ int partialLineCount = Compatibility.ceil(getClientArea().height, lineHeight); >+ return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1; > } > /** >- * Returns the content in the specified range using the platform line >+ * Returns the content in the specified range using the platform line > * delimiter to separate lines. > * <p> > * > * @param writer the TextWriter to write line text into >- * @return the content in the specified range using the platform line >- * delimiter to separate lines as written by the specified TextWriter. >+ * @return the content in the specified range using the platform line >+ * delimiter to separate lines as written by the specified TextWriter. > */ > String getPlatformDelimitedText(TextWriter writer) { >- int end = writer.getStart() + writer.getCharCount(); >- int startLine = logicalContent.getLineAtOffset(writer.getStart()); >- int endLine = logicalContent.getLineAtOffset(end); >- String endLineText = logicalContent.getLine(endLine); >- int endLineOffset = logicalContent.getOffsetAtLine(endLine); >- >- for (int i = startLine; i <= endLine; i++) { >- writer.writeLine(logicalContent.getLine(i), logicalContent.getOffsetAtLine(i)); >- if (i < endLine) { >- writer.writeLineDelimiter(PlatformLineDelimiter); >- } >- } >- if (end > endLineOffset + endLineText.length()) { >- writer.writeLineDelimiter(PlatformLineDelimiter); >- } >- writer.close(); >- return writer.toString(); >+ int end = writer.getStart() + writer.getCharCount(); >+ int startLine = logicalContent.getLineAtOffset(writer.getStart()); >+ int endLine = logicalContent.getLineAtOffset(end); >+ String endLineText = logicalContent.getLine(endLine); >+ int endLineOffset = logicalContent.getOffsetAtLine(endLine); >+ >+ for (int i = startLine; i <= endLine; i++) { >+ writer.writeLine(logicalContent.getLine(i), logicalContent.getOffsetAtLine(i)); >+ if (i < endLine) { >+ writer.writeLineDelimiter(PlatformLineDelimiter); >+ } >+ } >+ if (end > endLineOffset + endLineText.length()) { >+ writer.writeLineDelimiter(PlatformLineDelimiter); >+ } >+ writer.close(); >+ return writer.toString(); > } > /** > * Returns the selection. > * <p> > * Text selections are specified in terms of caret positions. In a text >- * widget that contains N characters, there are N+1 caret positions, >+ * widget that contains N characters, there are N+1 caret positions, > * ranging from 0..N > * <p> > * >- * @return start and end of the selection, x is the offset of the first >- * selected character, y is the offset after the last selected character. >- * The selection values returned are visual (i.e., x will always always be >- * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right >- * (LtoR), compare the caretOffset to the start and end of the selection >+ * @return start and end of the selection, x is the offset of the first >+ * selected character, y is the offset after the last selected character. >+ * The selection values returned are visual (i.e., x will always always be >+ * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right >+ * (LtoR), compare the caretOffset to the start and end of the selection > * (e.g., caretOffset == start of selection implies that the selection is RtoL). > * @see #getSelectionRange > * @exception SWTException <ul> >@@ -4130,19 +4140,19 @@ > * </ul> > */ > public Point getSelection() { >- checkWidget(); >- return new Point(selection.x, selection.y); >+ checkWidget(); >+ return new Point(selection.x, selection.y); > } > /** > * Returns the selection. > * <p> > * >- * @return start and length of the selection, x is the offset of the >- * first selected character, relative to the first character of the >- * widget content. y is the length of the selection. >- * The selection values returned are visual (i.e., length will always always be >- * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right >- * (LtoR), compare the caretOffset to the start and end of the selection >+ * @return start and length of the selection, x is the offset of the >+ * first selected character, relative to the first character of the >+ * widget content. y is the length of the selection. >+ * The selection values returned are visual (i.e., length will always always be >+ * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right >+ * (LtoR), compare the caretOffset to the start and end of the selection > * (e.g., caretOffset == start of selection implies that the selection is RtoL). > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -4150,8 +4160,8 @@ > * </ul> > */ > public Point getSelectionRange() { >- checkWidget(); >- return new Point(selection.x, selection.y - selection.x); >+ checkWidget(); >+ return new Point(selection.x, selection.y - selection.x); > } > /** > * Returns the receiver's selection background color. >@@ -4165,11 +4175,11 @@ > * @since 2.1 > */ > public Color getSelectionBackground() { >- checkWidget(); >- if (selectionBackground == null) { >- return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION); >- } >- return selectionBackground; >+ checkWidget(); >+ if (selectionBackground == null) { >+ return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION); >+ } >+ return selectionBackground; > } > /** > * Gets the number of selected characters. >@@ -4182,8 +4192,8 @@ > * </ul> > */ > public int getSelectionCount() { >- checkWidget(); >- return getSelectionRange().y; >+ checkWidget(); >+ return getSelectionRange().y; > } > /** > * Returns the receiver's selection foreground color. >@@ -4197,11 +4207,11 @@ > * @since 2.1 > */ > public Color getSelectionForeground() { >- checkWidget(); >- if (selectionForeground == null) { >- return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT); >- } >- return selectionForeground; >+ checkWidget(); >+ if (selectionForeground == null) { >+ return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT); >+ } >+ return selectionForeground; > } > /** > * Returns the selected text. >@@ -4214,129 +4224,129 @@ > * </ul> > */ > public String getSelectionText() { >- checkWidget(); >- return content.getTextRange(selection.x, selection.y - selection.x); >+ checkWidget(); >+ return content.getTextRange(selection.x, selection.y - selection.x); > } > /** >- * Returns the text segments that should be treated as if they >+ * Returns the text segments that should be treated as if they > * had a different direction than the surrounding text. > * <p> > * >- * @param lineOffset offset of the first character in the line. >- * 0 based from the beginning of the document. >+ * @param lineOffset offset of the first character in the line. >+ * 0 based from the beginning of the document. > * @param line text of the line to specify bidi segments for > * @return text segments that should be treated as if they had a >- * different direction than the surrounding text. Only the start >- * index of a segment is specified, relative to the start of the >- * line. Always starts with 0 and ends with the line length. >- * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_ARGUMENT - if the segment indices returned >- * by the listener do not start with 0, are not in ascending order, >- * exceed the line length or have duplicates</li> >+ * different direction than the surrounding text. Only the start >+ * index of a segment is specified, relative to the start of the >+ * line. Always starts with 0 and ends with the line length. >+ * @exception IllegalArgumentException <ul> >+ * <li>ERROR_INVALID_ARGUMENT - if the segment indices returned >+ * by the listener do not start with 0, are not in ascending order, >+ * exceed the line length or have duplicates</li> > * </ul> > */ > int [] getBidiSegments(int lineOffset, String line) { >- if (isListening(LineGetSegments) == false) { >- return getBidiSegmentsCompatibility(line, lineOffset); >- } >- StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line); >- int lineLength = line.length(); >- int[] segments; >- if (event == null || event.segments == null || event.segments.length == 0) { >- segments = new int[] {0, lineLength}; >- } >- else { >- int segmentCount = event.segments.length; >- >- // test segment index consistency >- if (event.segments[0] != 0) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- for (int i = 1; i < segmentCount; i++) { >- if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- } >- // ensure that last segment index is line end offset >- if (event.segments[segmentCount - 1] != lineLength) { >- segments = new int[segmentCount + 1]; >- System.arraycopy(event.segments, 0, segments, 0, segmentCount); >- segments[segmentCount] = lineLength; >- } >- else { >- segments = event.segments; >- } >- } >- return segments; >+ if (isListening(LineGetSegments) == false) { >+ return getBidiSegmentsCompatibility(line, lineOffset); >+ } >+ StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line); >+ int lineLength = line.length(); >+ int[] segments; >+ if (event == null || event.segments == null || event.segments.length == 0) { >+ segments = new int[] {0, lineLength}; >+ } >+ else { >+ int segmentCount = event.segments.length; >+ >+ // test segment index consistency >+ if (event.segments[0] != 0) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ for (int i = 1; i < segmentCount; i++) { >+ if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ } >+ // ensure that last segment index is line end offset >+ if (event.segments[segmentCount - 1] != lineLength) { >+ segments = new int[segmentCount + 1]; >+ System.arraycopy(event.segments, 0, segments, 0, segmentCount); >+ segments[segmentCount] = lineLength; >+ } >+ else { >+ segments = event.segments; >+ } >+ } >+ return segments; > } > /** > * @see getBidiSegments > * Supports deprecated setBidiColoring API. Remove when API is removed. > */ > int [] getBidiSegmentsCompatibility(String line, int lineOffset) { >- StyledTextEvent event; >- StyleRange [] styles = new StyleRange [0]; >- int lineLength = line.length(); >- if (bidiColoring == false) { >- return new int[] {0, lineLength}; >- } >- event = renderer.getLineStyleData(lineOffset, line); >- if (event != null) { >- styles = event.styles; >- } >- if (styles.length == 0) { >- return new int[] {0, lineLength}; >- } >- int k=0, count = 1; >- while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) { >- k++; >- } >- int[] offsets = new int[(styles.length - k) * 2 + 2]; >- for (int i = k; i < styles.length; i++) { >- StyleRange style = styles[i]; >- int styleLineStart = Math.max(style.start - lineOffset, 0); >- int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart); >- styleLineEnd = Math.min (styleLineEnd, line.length ()); >- if (i > 0 && count > 1 && >- ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) || >- (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) && >- style.similarTo(styles[i-1])) { >- offsets[count-2] = Math.min(offsets[count-2], styleLineStart); >- offsets[count-1] = Math.max(offsets[count-1], styleLineEnd); >- } else { >- if (styleLineStart > offsets[count - 1]) { >- offsets[count] = styleLineStart; >- count++; >- } >- offsets[count] = styleLineEnd; >- count++; >- } >- } >- // add offset for last non-colored segment in line, if any >- if (lineLength > offsets[count-1]) { >- offsets [count] = lineLength; >- count++; >- } >- if (count == offsets.length) { >- return offsets; >- } >- int [] result = new int [count]; >- System.arraycopy (offsets, 0, result, 0, count); >- return result; >+ StyledTextEvent event; >+ StyleRange [] styles = new StyleRange [0]; >+ int lineLength = line.length(); >+ if (bidiColoring == false) { >+ return new int[] {0, lineLength}; >+ } >+ event = renderer.getLineStyleData(lineOffset, line); >+ if (event != null) { >+ styles = event.styles; >+ } >+ if (styles.length == 0) { >+ return new int[] {0, lineLength}; >+ } >+ int k=0, count = 1; >+ while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) { >+ k++; >+ } >+ int[] offsets = new int[(styles.length - k) * 2 + 2]; >+ for (int i = k; i < styles.length; i++) { >+ StyleRange style = styles[i]; >+ int styleLineStart = Math.max(style.start - lineOffset, 0); >+ int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart); >+ styleLineEnd = Math.min (styleLineEnd, line.length ()); >+ if (i > 0 && count > 1 && >+ ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) || >+ (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) && >+ style.similarTo(styles[i-1])) { >+ offsets[count-2] = Math.min(offsets[count-2], styleLineStart); >+ offsets[count-1] = Math.max(offsets[count-1], styleLineEnd); >+ } else { >+ if (styleLineStart > offsets[count - 1]) { >+ offsets[count] = styleLineStart; >+ count++; >+ } >+ offsets[count] = styleLineEnd; >+ count++; >+ } >+ } >+ // add offset for last non-colored segment in line, if any >+ if (lineLength > offsets[count-1]) { >+ offsets [count] = lineLength; >+ count++; >+ } >+ if (count == offsets.length) { >+ return offsets; >+ } >+ int [] result = new int [count]; >+ System.arraycopy (offsets, 0, result, 0, count); >+ return result; > } > /** > * Returns the style range at the given offset. > * Returns null if a LineStyleListener has been set or if a style is not set >- * for the offset. >- * Should not be called if a LineStyleListener has been set since the >+ * for the offset. >+ * Should not be called if a LineStyleListener has been set since the > * listener maintains the styles. > * <p> > * >- * @param offset the offset to return the style for. >- * 0 <= offset < getCharCount() must be true. >+ * @param offset the offset to return the style for. >+ * 0 <= offset < getCharCount() must be true. > * @return a StyleRange with start == offset and length == 1, indicating >- * the style at the given offset. null if a LineStyleListener has been set >- * or if a style is not set for the given offset. >+ * the style at the given offset. null if a LineStyleListener has been set >+ * or if a style is not set for the given offset. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> >@@ -4346,19 +4356,19 @@ > * </ul> > */ > public StyleRange getStyleRangeAtOffset(int offset) { >- checkWidget(); >- if (offset < 0 || offset >= getCharCount()) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- if (userLineStyle == false) { >- return defaultLineStyler.getStyleRangeAtOffset(offset); >- } >- return null; >+ checkWidget(); >+ if (offset < 0 || offset >= getCharCount()) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ if (userLineStyle == false) { >+ return defaultLineStyler.getStyleRangeAtOffset(offset); >+ } >+ return null; > } > /** > * Returns the styles. >- * Returns an empty array if a LineStyleListener has been set. >- * Should not be called if a LineStyleListener has been set since the >+ * Returns an empty array if a LineStyleListener has been set. >+ * Should not be called if a LineStyleListener has been set since the > * listener maintains the styles. > * <p> > * >@@ -4369,47 +4379,47 @@ > * </ul> > */ > public StyleRange [] getStyleRanges() { >- checkWidget(); >- StyleRange styles[]; >- >- if (userLineStyle == false) { >- styles = defaultLineStyler.getStyleRanges(); >- } >- else { >- styles = new StyleRange[0]; >- } >- return styles; >+ checkWidget(); >+ StyleRange styles[]; >+ >+ if (userLineStyle == false) { >+ styles = defaultLineStyler.getStyleRanges(); >+ } >+ else { >+ styles = new StyleRange[0]; >+ } >+ return styles; > } > /** > * Returns a StyledTextBidi object for the specified line. > * <p> >- * >- * @param lineText the line that the StyledTextBidi object should >- * work on. >- * @param lineOffset offset of the beginning of the line, relative >- * to the beginning of the document >+ * >+ * @param lineText the line that the StyledTextBidi object should >+ * work on. >+ * @param lineOffset offset of the beginning of the line, relative >+ * to the beginning of the document > * @param gc GC to use when creating a new StyledTextBidi object. > * @return a StyledTextBidi object for the specified line. > */ > StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc) { >- return getStyledTextBidi(lineText, lineOffset, gc, null); >+ return getStyledTextBidi(lineText, lineOffset, gc, null); > } > /** > * Returns a StyledTextBidi object for the specified line. > * <p> >- * >- * @param lineText the line that the StyledTextBidi object should >- * work on. >- * @param lineOffset offset of the beginning of the line, relative >- * to the beginning of the document >+ * >+ * @param lineText the line that the StyledTextBidi object should >+ * work on. >+ * @param lineOffset offset of the beginning of the line, relative >+ * to the beginning of the document > * @param gc GC to use when creating a new StyledTextBidi object. >- * @param styles StyleRanges to use when creating a new StyledTextBidi >- * object. >+ * @param styles StyleRanges to use when creating a new StyledTextBidi >+ * object. > * @return a StyledTextBidi object for the specified line. > */ > StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc, StyleRange[] styles) { >- return renderer.getStyledTextBidi(lineText, lineOffset, gc, styles); >-} >+ return renderer.getStyledTextBidi(lineText, lineOffset, gc, styles); >+} > /** > * Returns the tab width measured in characters. > * >@@ -4420,8 +4430,8 @@ > * </ul> > */ > public int getTabs() { >- checkWidget(); >- return tabLength; >+ checkWidget(); >+ return tabLength; > } > /** > * Returns a copy of the widget content. >@@ -4434,15 +4444,15 @@ > * </ul> > */ > public String getText() { >- checkWidget(); >- return content.getTextRange(0, getCharCount()); >-} >+ checkWidget(); >+ return content.getTextRange(0, getCharCount()); >+} > /** > * Returns the widget content between the two offsets. > * <p> > * > * @param start offset of the first character in the returned String >- * @param end offset of the last character in the returned String >+ * @param end offset of the last character in the returned String > * @return widget content starting at start and ending at end > * @see #getTextRange(int,int) > * @exception SWTException <ul> >@@ -4450,45 +4460,45 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> >+ * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> > * </ul> > */ > public String getText(int start, int end) { >- checkWidget(); >- int contentLength = getCharCount(); >- >- if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- return content.getTextRange(start, end - start + 1); >+ checkWidget(); >+ int contentLength = getCharCount(); >+ >+ if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ return content.getTextRange(start, end - start + 1); > } > /** > * Returns the widget content starting at start for length characters. > * <p> > * > * @param start offset of the first character in the returned String >- * @param length number of characters to return >+ * @param length number of characters to return > * @return widget content starting at start and extending length characters. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li> >+ * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li> > * </ul> > */ > public String getTextRange(int start, int length) { >- checkWidget(); >- int contentLength = getCharCount(); >- int end = start + length; >- >- if (start > end || start < 0 || end > contentLength) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- return content.getTextRange(start, length); >+ checkWidget(); >+ int contentLength = getCharCount(); >+ int end = start + length; >+ >+ if (start > end || start < 0 || end > contentLength) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ return content.getTextRange(start, length); > } > /** >- * Gets the text limit. The text limit specifies the amount of text that the user >+ * Gets the text limit. The text limit specifies the amount of text that the user > * can type into the widget. > * <p> > * >@@ -4498,51 +4508,51 @@ > * </ul> > */ > public int getTextLimit() { >- checkWidget(); >- >- return textLimit; >+ checkWidget(); >+ >+ return textLimit; > } > /** >- * Returns the x position of the character at the specified offset >+ * Returns the x position of the character at the specified offset > * relative to the first character in the line. > * Expands tabs to tab stops using the widget tab width. > * <p> > * > * @param line line to be measured. >- * @param lineIndex index of the line relative to the first kine of the >- * document >- * @param length number of characters to measure. Tabs are counted >- * as one character in this parameter. >+ * @param lineIndex index of the line relative to the first kine of the >+ * document >+ * @param length number of characters to measure. Tabs are counted >+ * as one character in this parameter. > * @param gc GC to use for measuring text >- * @return x position of the character at the specified offset >- * with tabs expanded to tab stops. 0 if the length is outside the >- * specified text. >+ * @return x position of the character at the specified offset >+ * with tabs expanded to tab stops. 0 if the length is outside the >+ * specified text. > */ > int getTextPosition(String line, int lineIndex, int length, GC gc) { >- int lineOffset = content.getOffsetAtLine(lineIndex); >- int lineLength = line.length(); >- int width; >- if (lineLength == 0 || length > lineLength) { >- return 0; >- } >- if (isBidi()) { >- StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc, null); >- width = getBidiTextPosition(line, length, bidi); >- } >- else { >- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >- StyleRange[] styles = null; >- if (event != null) { >- styles = renderer.filterLineStyles(event.styles); >- } >- width = renderer.getTextPosition(line, lineOffset, length, styles, gc); >- } >- return width; >+ int lineOffset = content.getOffsetAtLine(lineIndex); >+ int lineLength = line.length(); >+ int width; >+ if (lineLength == 0 || length > lineLength) { >+ return 0; >+ } >+ if (isBidi()) { >+ StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc, null); >+ width = getBidiTextPosition(line, length, bidi); >+ } >+ else { >+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line); >+ StyleRange[] styles = null; >+ if (event != null) { >+ styles = renderer.filterLineStyles(event.styles); >+ } >+ width = renderer.getTextPosition(line, lineOffset, length, styles, gc); >+ } >+ return width; > } > /** > * Gets the top index. The top index is the index of the fully visible line that >- * is currently at the top of the widget or the topmost partially visible line if >- * no line is fully visible. >+ * is currently at the top of the widget or the topmost partially visible line if >+ * no line is fully visible. > * The top index changes when the widget is scrolled. Indexing is zero based. > * <p> > * >@@ -4553,20 +4563,20 @@ > * </ul> > */ > public int getTopIndex() { >- checkWidget(); >- int logicalTopIndex = topIndex; >- >- if (wordWrap) { >- int visualLineOffset = content.getOffsetAtLine(topIndex); >- logicalTopIndex = logicalContent.getLineAtOffset(visualLineOffset); >- } >- return logicalTopIndex; >+ checkWidget(); >+ int logicalTopIndex = topIndex; >+ >+ if (wordWrap) { >+ int visualLineOffset = content.getOffsetAtLine(topIndex); >+ logicalTopIndex = logicalContent.getLineAtOffset(visualLineOffset); >+ } >+ return logicalTopIndex; > } > /** >- * Gets the top pixel. The top pixel is the pixel position of the line that is >- * currently at the top of the widget.The text widget can be scrolled by pixels >- * by dragging the scroll thumb so that a partial line may be displayed at the top >- * the widget. The top pixel changes when the widget is scrolled. The top pixel >+ * Gets the top pixel. The top pixel is the pixel position of the line that is >+ * currently at the top of the widget.The text widget can be scrolled by pixels >+ * by dragging the scroll thumb so that a partial line may be displayed at the top >+ * the widget. The top pixel changes when the widget is scrolled. The top pixel > * does not include the widget trimming. > * <p> > * >@@ -4577,39 +4587,39 @@ > * </ul> > */ > public int getTopPixel() { >- checkWidget(); >- return verticalScrollOffset; >+ checkWidget(); >+ return verticalScrollOffset; > } >-/** >+/** > * Returns the vertical scroll increment. > * <p> > * > * @return vertical scroll increment. > */ > int getVerticalIncrement() { >- return lineHeight; >+ return lineHeight; > } > /** > * Returns the index of the line the caret is on. >- * When in word wrap mode and at the end of one wrapped line/ >+ * When in word wrap mode and at the end of one wrapped line/ > * beginning of the continuing wrapped line the caret offset > * is not sufficient to determine the caret line. >- * >+ * > * @return the index of the line the caret is on. > */ > int getCaretLine() { >- int caretLine = content.getLineAtOffset(caretOffset); >- int leftColumnX = 0; >- >- if (isBidi()) { >- leftColumnX = XINSET; >- } >- if (wordWrap && columnX <= leftColumnX && >- caretLine < content.getLineCount() - 1 && >- caretOffset == content.getOffsetAtLine(caretLine + 1)) { >- caretLine++; >- } >- return caretLine; >+ int caretLine = content.getLineAtOffset(caretOffset); >+ int leftColumnX = 0; >+ >+ if (isBidi()) { >+ leftColumnX = XINSET; >+ } >+ if (wordWrap && columnX <= leftColumnX && >+ caretLine < content.getLineCount() - 1 && >+ caretOffset == content.getOffsetAtLine(caretLine + 1)) { >+ caretLine++; >+ } >+ return caretLine; > } > /** > * Returns the offset of the character after the word at the specified >@@ -4623,42 +4633,42 @@ > * </p> > * <p> > * Space characters ' ' (ASCII 20) are special as they are treated as >- * part of the word leading up to the space character. Line breaks are >+ * part of the word leading up to the space character. Line breaks are > * treated as one word. > * </p> > */ > int getWordEnd(int offset) { >- int line = logicalContent.getLineAtOffset(offset); >- int lineOffset = logicalContent.getOffsetAtLine(line); >- String lineText = logicalContent.getLine(line); >- int lineLength = lineText.length(); >- >- if (offset >= getCharCount()) { >- return offset; >- } >- if (offset == lineOffset + lineLength) { >- line++; >- offset = logicalContent.getOffsetAtLine(line); >- } >- else { >- offset -= lineOffset; >- char ch = lineText.charAt(offset); >- boolean letterOrDigit = Compatibility.isLetterOrDigit(ch); >- while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit) { >- offset++; >- ch = lineText.charAt(offset); >- } >- // skip over trailing whitespace >- while (offset < lineLength - 1 && Compatibility.isSpaceChar(ch)) { >- offset++; >- ch = lineText.charAt(offset); >- } >- if (offset == lineLength - 1 && (Compatibility.isLetterOrDigit(ch) == letterOrDigit || Compatibility.isSpaceChar(ch))) { >- offset++; >- } >- offset += lineOffset; >- } >- return offset; >+ int line = logicalContent.getLineAtOffset(offset); >+ int lineOffset = logicalContent.getOffsetAtLine(line); >+ String lineText = logicalContent.getLine(line); >+ int lineLength = lineText.length(); >+ >+ if (offset >= getCharCount()) { >+ return offset; >+ } >+ if (offset == lineOffset + lineLength) { >+ line++; >+ offset = logicalContent.getOffsetAtLine(line); >+ } >+ else { >+ offset -= lineOffset; >+ char ch = lineText.charAt(offset); >+ boolean letterOrDigit = Compatibility.isLetterOrDigit(ch); >+ while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit) { >+ offset++; >+ ch = lineText.charAt(offset); >+ } >+ // skip over trailing whitespace >+ while (offset < lineLength - 1 && Compatibility.isSpaceChar(ch)) { >+ offset++; >+ ch = lineText.charAt(offset); >+ } >+ if (offset == lineLength - 1 && (Compatibility.isLetterOrDigit(ch) == letterOrDigit || Compatibility.isSpaceChar(ch))) { >+ offset++; >+ } >+ offset += lineOffset; >+ } >+ return offset; > } > /** > * Returns the offset of the character after the word at the specified >@@ -4671,38 +4681,38 @@ > * </ul> > * </p> > * <p> >- * Spaces are ignored and do not represent a word. Line breaks are treated >+ * Spaces are ignored and do not represent a word. Line breaks are treated > * as one word. > * </p> > */ > int getWordEndNoSpaces(int offset) { >- int line = logicalContent.getLineAtOffset(offset); >- int lineOffset = logicalContent.getOffsetAtLine(line); >- String lineText = logicalContent.getLine(line); >- int lineLength = lineText.length(); >- >- if (offset >= getCharCount()) { >- return offset; >- } >- if (offset == lineOffset + lineLength) { >- line++; >- offset = logicalContent.getOffsetAtLine(line); >- } >- else { >- offset -= lineOffset; >- char ch = lineText.charAt(offset); >- boolean letterOrDigit = Compatibility.isLetterOrDigit(ch); >- >- while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) { >- offset++; >- ch = lineText.charAt(offset); >- } >- if (offset == lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) { >- offset++; >- } >- offset += lineOffset; >- } >- return offset; >+ int line = logicalContent.getLineAtOffset(offset); >+ int lineOffset = logicalContent.getOffsetAtLine(line); >+ String lineText = logicalContent.getLine(line); >+ int lineLength = lineText.length(); >+ >+ if (offset >= getCharCount()) { >+ return offset; >+ } >+ if (offset == lineOffset + lineLength) { >+ line++; >+ offset = logicalContent.getOffsetAtLine(line); >+ } >+ else { >+ offset -= lineOffset; >+ char ch = lineText.charAt(offset); >+ boolean letterOrDigit = Compatibility.isLetterOrDigit(ch); >+ >+ while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) { >+ offset++; >+ ch = lineText.charAt(offset); >+ } >+ if (offset == lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) { >+ offset++; >+ } >+ offset += lineOffset; >+ } >+ return offset; > } > /** > * Returns the start offset of the word at the specified offset. >@@ -4715,44 +4725,44 @@ > * </p> > * <p> > * Space characters ' ' (ASCII 20) are special as they are treated as >- * part of the word leading up to the space character. Line breaks are treated >+ * part of the word leading up to the space character. Line breaks are treated > * as one word. > * </p> > */ > int getWordStart(int offset) { >- int line = logicalContent.getLineAtOffset(offset); >- int lineOffset = logicalContent.getOffsetAtLine(line); >- String lineText = logicalContent.getLine(line); >- >- if (offset <= 0) { >- return offset; >- } >- if (offset == lineOffset) { >- line--; >- lineText = logicalContent.getLine(line); >- offset = logicalContent.getOffsetAtLine(line) + lineText.length(); >- } >- else { >- char ch; >- boolean letterOrDigit; >- >- offset -= lineOffset; >- // skip over trailing whitespace >- do { >- offset--; >- ch = lineText.charAt(offset); >- } while (offset > 0 && Compatibility.isSpaceChar(ch)); >- letterOrDigit = Compatibility.isLetterOrDigit(ch); >- while (offset > 0 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) { >- offset--; >- ch = lineText.charAt(offset); >- } >- if (offset > 0 || Compatibility.isLetterOrDigit(ch) != letterOrDigit) { >- offset++; >- } >- offset += lineOffset; >- } >- return offset; >+ int line = logicalContent.getLineAtOffset(offset); >+ int lineOffset = logicalContent.getOffsetAtLine(line); >+ String lineText = logicalContent.getLine(line); >+ >+ if (offset <= 0) { >+ return offset; >+ } >+ if (offset == lineOffset) { >+ line--; >+ lineText = logicalContent.getLine(line); >+ offset = logicalContent.getOffsetAtLine(line) + lineText.length(); >+ } >+ else { >+ char ch; >+ boolean letterOrDigit; >+ >+ offset -= lineOffset; >+ // skip over trailing whitespace >+ do { >+ offset--; >+ ch = lineText.charAt(offset); >+ } while (offset > 0 && Compatibility.isSpaceChar(ch)); >+ letterOrDigit = Compatibility.isLetterOrDigit(ch); >+ while (offset > 0 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) { >+ offset--; >+ ch = lineText.charAt(offset); >+ } >+ if (offset > 0 || Compatibility.isLetterOrDigit(ch) != letterOrDigit) { >+ offset++; >+ } >+ offset += lineOffset; >+ } >+ return offset; > } > /** > * Returns whether the widget wraps lines. >@@ -4762,10 +4772,10 @@ > * @since 2.0 > */ > public boolean getWordWrap() { >- checkWidget(); >- return wordWrap; >+ checkWidget(); >+ return wordWrap; > } >-/** >+/** > * Returns the x location of the character at the give offset in the line. > * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts). > * <p> >@@ -4773,24 +4783,24 @@ > * @return x location of the character at the given offset in the line. > */ > int getXAtOffset(String line, int lineIndex, int lineOffset) { >- int x; >- if (lineOffset == 0 && isBidi() == false) { >- x = leftMargin; >- } >- else { >- GC gc = getGC(); >- x = getTextPosition(line, lineIndex, Math.min(line.length(), lineOffset), gc) + leftMargin; >- gc.dispose(); >- if (lineOffset > line.length()) { >- // offset is not on the line. return an x location one character >- // after the line to indicate the line delimiter. >- x += lineEndSpaceWidth; >- } >- } >- return x - horizontalScrollOffset; >+ int x; >+ if (lineOffset == 0 && isBidi() == false) { >+ x = leftMargin; >+ } >+ else { >+ GC gc = getGC(); >+ x = getTextPosition(line, lineIndex, Math.min(line.length(), lineOffset), gc) + leftMargin; >+ gc.dispose(); >+ if (lineOffset > line.length()) { >+ // offset is not on the line. return an x location one character >+ // after the line to indicate the line delimiter. >+ x += lineEndSpaceWidth; >+ } >+ } >+ return x - horizontalScrollOffset; > } >-/** >- * Inserts a string. The old selection is replaced with the new text. >+/** >+ * Inserts a string. The old selection is replaced with the new text. > * <p> > * > * @param string the string >@@ -4804,30 +4814,30 @@ > * </ul> > */ > public void insert(String string) { >- checkWidget(); >- if (string == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- Point sel = getSelectionRange(); >- replaceTextRange(sel.x, sel.y, string); >+ checkWidget(); >+ if (string == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ Point sel = getSelectionRange(); >+ replaceTextRange(sel.x, sel.y, string); > } > /** > * Creates content change listeners and set the default content model. > */ > void installDefaultContent() { >- textChangeListener = new TextChangeListener() { >- public void textChanging(TextChangingEvent event) { >- handleTextChanging(event); >- } >- public void textChanged(TextChangedEvent event) { >- handleTextChanged(event); >- } >- public void textSet(TextChangedEvent event) { >- handleTextSet(event); >- } >- }; >- logicalContent = content = new DefaultContent(); >- content.addTextChangeListener(textChangeListener); >+ textChangeListener = new TextChangeListener() { >+ public void textChanging(TextChangingEvent event) { >+ handleTextChanging(event); >+ } >+ public void textChanged(TextChangedEvent event) { >+ handleTextChanged(event); >+ } >+ public void textSet(TextChangedEvent event) { >+ handleTextSet(event); >+ } >+ }; >+ logicalContent = content = new DefaultContent(); >+ content.addTextChangeListener(textChangeListener); > } > /** > * Creates a default line style listener. >@@ -4838,159 +4848,159 @@ > * @see #addLineStyleListener > */ > void installDefaultLineStyler() { >- defaultLineStyler = new DefaultLineStyler(logicalContent); >- StyledTextListener typedListener = new StyledTextListener(defaultLineStyler); >- if (userLineStyle == false) { >- addListener(LineGetStyle, typedListener); >- } >- if (userLineBackground == false) { >- addListener(LineGetBackground, typedListener); >- } >+ defaultLineStyler = new DefaultLineStyler(logicalContent); >+ StyledTextListener typedListener = new StyledTextListener(defaultLineStyler); >+ if (userLineStyle == false) { >+ addListener(LineGetStyle, typedListener); >+ } >+ if (userLineBackground == false) { >+ addListener(LineGetBackground, typedListener); >+ } > } >-/** >+/** > * Adds event listeners > */ > void installListeners() { >- ScrollBar verticalBar = getVerticalBar(); >- ScrollBar horizontalBar = getHorizontalBar(); >- >- addListener(SWT.Dispose, new Listener() { >- public void handleEvent(Event event) { >- handleDispose(); >- } >- }); >- addListener(SWT.KeyDown, new Listener() { >- public void handleEvent(Event event) { >- handleKeyDown(event); >- } >- }); >- addListener(SWT.MouseDown, new Listener() { >- public void handleEvent(Event event) { >- handleMouseDown(event); >- } >- }); >- addListener(SWT.MouseUp, new Listener() { >- public void handleEvent(Event event) { >- handleMouseUp(event); >- } >- }); >- addListener(SWT.MouseDoubleClick, new Listener() { >- public void handleEvent(Event event) { >- handleMouseDoubleClick(event); >- } >- }); >- addListener(SWT.MouseMove, new Listener() { >- public void handleEvent(Event event) { >- handleMouseMove(event); >- } >- }); >- addListener(SWT.Paint, new Listener() { >- public void handleEvent(Event event) { >- handlePaint(event); >- } >- }); >- addListener(SWT.Resize, new Listener() { >- public void handleEvent(Event event) { >- handleResize(event); >- } >- }); >- addListener(SWT.Traverse, new Listener() { >- public void handleEvent(Event event) { >- handleTraverse(event); >- } >- }); >- if (verticalBar != null) { >- verticalBar.addListener(SWT.Selection, new Listener() { >- public void handleEvent(Event event) { >- handleVerticalScroll(event); >- } >- }); >- } >- if (horizontalBar != null) { >- horizontalBar.addListener(SWT.Selection, new Listener() { >- public void handleEvent(Event event) { >- handleHorizontalScroll(event); >- } >- }); >- } >+ ScrollBar verticalBar = getVerticalBar(); >+ ScrollBar horizontalBar = getHorizontalBar(); >+ >+ addListener(SWT.Dispose, new Listener() { >+ public void handleEvent(Event event) { >+ handleDispose(); >+ } >+ }); >+ addListener(SWT.KeyDown, new Listener() { >+ public void handleEvent(Event event) { >+ handleKeyDown(event); >+ } >+ }); >+ addListener(SWT.MouseDown, new Listener() { >+ public void handleEvent(Event event) { >+ handleMouseDown(event); >+ } >+ }); >+ addListener(SWT.MouseUp, new Listener() { >+ public void handleEvent(Event event) { >+ handleMouseUp(event); >+ } >+ }); >+ addListener(SWT.MouseDoubleClick, new Listener() { >+ public void handleEvent(Event event) { >+ handleMouseDoubleClick(event); >+ } >+ }); >+ addListener(SWT.MouseMove, new Listener() { >+ public void handleEvent(Event event) { >+ handleMouseMove(event); >+ } >+ }); >+ addListener(SWT.Paint, new Listener() { >+ public void handleEvent(Event event) { >+ handlePaint(event); >+ } >+ }); >+ addListener(SWT.Resize, new Listener() { >+ public void handleEvent(Event event) { >+ handleResize(event); >+ } >+ }); >+ addListener(SWT.Traverse, new Listener() { >+ public void handleEvent(Event event) { >+ handleTraverse(event); >+ } >+ }); >+ if (verticalBar != null) { >+ verticalBar.addListener(SWT.Selection, new Listener() { >+ public void handleEvent(Event event) { >+ handleVerticalScroll(event); >+ } >+ }); >+ } >+ if (horizontalBar != null) { >+ horizontalBar.addListener(SWT.Selection, new Listener() { >+ public void handleEvent(Event event) { >+ handleHorizontalScroll(event); >+ } >+ }); >+ } > } > StyledTextContent internalGetContent() { >- return content; >+ return content; > } > int internalGetHorizontalPixel() { >- return horizontalScrollOffset; >+ return horizontalScrollOffset; > } > LineCache internalGetLineCache() { >- return lineCache; >+ return lineCache; > } > Point internalGetSelection() { >- return selection; >+ return selection; > } > boolean internalGetWordWrap() { >- return wordWrap; >+ return wordWrap; > } > /** > * Used by WordWrapCache to bypass StyledText.redraw which does > * an unwanted cache reset. > */ > void internalRedraw() { >- super.redraw(); >+ super.redraw(); > } >-/** >+/** > * Redraws the specified text range. > * <p> > * > * @param start offset of the first character to redraw > * @param length number of characters to redraw >- * @param clearBackground true if the background should be cleared as >- * part of the redraw operation. If true, the entire redraw range will >+ * @param clearBackground true if the background should be cleared as >+ * part of the redraw operation. If true, the entire redraw range will > * be cleared before anything is redrawn. If the redraw range includes >- * the last character of a line (i.e., the entire line is redrawn) the >- * line is cleared all the way to the right border of the widget. >- * The redraw operation will be faster and smoother if clearBackground is >- * set to false. Whether or not the flag can be set to false depends on >- * the type of change that has taken place. If font styles or background >- * colors for the redraw range have changed, clearBackground should be >- * set to true. If only foreground colors have changed for the redraw >- * range, clearBackground can be set to false. >+ * the last character of a line (i.e., the entire line is redrawn) the >+ * line is cleared all the way to the right border of the widget. >+ * The redraw operation will be faster and smoother if clearBackground is >+ * set to false. Whether or not the flag can be set to false depends on >+ * the type of change that has taken place. If font styles or background >+ * colors for the redraw range have changed, clearBackground should be >+ * set to true. If only foreground colors have changed for the redraw >+ * range, clearBackground can be set to false. > */ > void internalRedrawRange(int start, int length, boolean clearBackground) { >- int end = start + length; >- int firstLine = content.getLineAtOffset(start); >- int lastLine = content.getLineAtOffset(end); >- int offsetInFirstLine; >- int partialBottomIndex = getPartialBottomIndex(); >- int partialTopIndex = verticalScrollOffset / lineHeight; >- // do nothing if redraw range is completely invisible >- if (firstLine > partialBottomIndex || lastLine < partialTopIndex) { >- return; >- } >- // only redraw visible lines >- if (partialTopIndex > firstLine) { >- firstLine = partialTopIndex; >- offsetInFirstLine = 0; >- } >- else { >- offsetInFirstLine = start - content.getOffsetAtLine(firstLine); >- } >- if (partialBottomIndex + 1 < lastLine) { >- lastLine = partialBottomIndex + 1; // + 1 to redraw whole bottom line, including line break >- end = content.getOffsetAtLine(lastLine); >- } >- // redraw first and last lines >- if (isBidi()) { >- redrawBidiLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground); >- } >- else { >- redrawLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground); >- } >- // redraw entire center lines if redraw range includes more than two lines >- if (lastLine - firstLine > 1) { >- Rectangle clientArea = getClientArea(); >- int redrawStopY = lastLine * lineHeight - verticalScrollOffset; >- int redrawY = (firstLine + 1) * lineHeight - verticalScrollOffset; >- draw(0, redrawY, clientArea.width, redrawStopY - redrawY, clearBackground); >- } >+ int end = start + length; >+ int firstLine = content.getLineAtOffset(start); >+ int lastLine = content.getLineAtOffset(end); >+ int offsetInFirstLine; >+ int partialBottomIndex = getPartialBottomIndex(); >+ int partialTopIndex = verticalScrollOffset / lineHeight; >+ // do nothing if redraw range is completely invisible >+ if (firstLine > partialBottomIndex || lastLine < partialTopIndex) { >+ return; >+ } >+ // only redraw visible lines >+ if (partialTopIndex > firstLine) { >+ firstLine = partialTopIndex; >+ offsetInFirstLine = 0; >+ } >+ else { >+ offsetInFirstLine = start - content.getOffsetAtLine(firstLine); >+ } >+ if (partialBottomIndex + 1 < lastLine) { >+ lastLine = partialBottomIndex + 1; // + 1 to redraw whole bottom line, including line break >+ end = content.getOffsetAtLine(lastLine); >+ } >+ // redraw first and last lines >+ if (isBidi()) { >+ redrawBidiLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground); >+ } >+ else { >+ redrawLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground); >+ } >+ // redraw entire center lines if redraw range includes more than two lines >+ if (lastLine - firstLine > 1) { >+ Rectangle clientArea = getClientArea(); >+ int redrawStopY = lastLine * lineHeight - verticalScrollOffset; >+ int redrawY = (firstLine + 1) * lineHeight - verticalScrollOffset; >+ draw(0, redrawY, clientArea.width, redrawStopY - redrawY, clearBackground); >+ } > } > /** > * Returns the widget text with style information encoded using RTF format >@@ -5003,41 +5013,41 @@ > * </ul> > */ > String getRtf(){ >- checkWidget(); >- RTFWriter rtfWriter = new RTFWriter(0, getCharCount()); >- return getPlatformDelimitedText(rtfWriter); >+ checkWidget(); >+ RTFWriter rtfWriter = new RTFWriter(0, getCharCount()); >+ return getPlatformDelimitedText(rtfWriter); > } >-/** >+/** > * Frees resources. > */ > void handleDispose() { >- clipboard.dispose(); >- ibeamCursor.dispose(); >- if (renderer != null) { >- renderer.dispose(); >- renderer = null; >- } >- if (content != null) { >- content.removeTextChangeListener(textChangeListener); >- } >- if (leftCaretBitmap != null) { >- leftCaretBitmap.dispose(); >- leftCaretBitmap = null; >- } >- if (rightCaretBitmap != null) { >- rightCaretBitmap.dispose(); >- rightCaretBitmap = null; >- } >- if (isBidi()) { >- StyledTextBidi.removeLanguageListener(this); >- } >+ clipboard.dispose(); >+ ibeamCursor.dispose(); >+ if (renderer != null) { >+ renderer.dispose(); >+ renderer = null; >+ } >+ if (content != null) { >+ content.removeTextChangeListener(textChangeListener); >+ } >+ if (leftCaretBitmap != null) { >+ leftCaretBitmap.dispose(); >+ leftCaretBitmap = null; >+ } >+ if (rightCaretBitmap != null) { >+ rightCaretBitmap.dispose(); >+ rightCaretBitmap = null; >+ } >+ if (isBidi()) { >+ StyledTextBidi.removeLanguageListener(this); >+ } > } >-/** >+/** > * Scrolls the widget horizontally. > */ > void handleHorizontalScroll(Event event) { >- int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset; >- scrollHorizontal(scrollPixel); >+ int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset; >+ scrollHorizontal(scrollPixel); > } > /** > * If an action has been registered for the key stroke execute the action. >@@ -5047,56 +5057,56 @@ > * @param event keyboard event > */ > void handleKey(Event event) { >- int action; >- >- if (event.keyCode != 0) { >- // special key pressed (e.g., F1) >- action = getKeyBinding(event.keyCode | event.stateMask); >- } >- else { >- // character key pressed >- action = getKeyBinding(event.character | event.stateMask); >- if (action == SWT.NULL) { >- // see if we have a control character >- if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) { >- // get the character from the CTRL+char sequence, the control >- // key subtracts 64 from the value of the key that it modifies >- int c = event.character + 64; >- action = getKeyBinding(c | event.stateMask); >- } >- } >- } >- if (action == SWT.NULL) { >- boolean ignore = false; >- >- if (isCarbon) { >- // Ignore acclerator key combinations (we do not want to >- // insert a character in the text in this instance). Do not >- // ignore COMMAND+ALT combinations since that key sequence >- // produces characters on the mac. >- ignore = (event.stateMask ^ SWT.COMMAND) == 0 || >- (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0; >- } else { >- // Ignore acclerator key combinations (we do not want to >- // insert a character in the text in this instance). Don't >- // ignore CTRL+ALT combinations since that is the Alt Gr >- // key on some keyboards. See bug 20953. >- ignore = (event.stateMask ^ SWT.ALT) == 0 || >- (event.stateMask ^ SWT.CTRL) == 0 || >- (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 || >- (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0; >- } >- // -ignore anything below SPACE except for line delimiter keys and tab. >- // -ignore DEL >- if (!ignore && event.character > 31 && event.character != SWT.DEL || >- event.character == SWT.CR || event.character == SWT.LF || >- event.character == TAB) { >- doContent(event.character); >- } >- } >- else { >- invokeAction(action); >- } >+ int action; >+ >+ if (event.keyCode != 0) { >+ // special key pressed (e.g., F1) >+ action = getKeyBinding(event.keyCode | event.stateMask); >+ } >+ else { >+ // character key pressed >+ action = getKeyBinding(event.character | event.stateMask); >+ if (action == SWT.NULL) { >+ // see if we have a control character >+ if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) { >+ // get the character from the CTRL+char sequence, the control >+ // key subtracts 64 from the value of the key that it modifies >+ int c = event.character + 64; >+ action = getKeyBinding(c | event.stateMask); >+ } >+ } >+ } >+ if (action == SWT.NULL) { >+ boolean ignore = false; >+ >+ if (isCarbon) { >+ // Ignore acclerator key combinations (we do not want to >+ // insert a character in the text in this instance). Do not >+ // ignore COMMAND+ALT combinations since that key sequence >+ // produces characters on the mac. >+ ignore = (event.stateMask ^ SWT.COMMAND) == 0 || >+ (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0; >+ } else { >+ // Ignore acclerator key combinations (we do not want to >+ // insert a character in the text in this instance). Don't >+ // ignore CTRL+ALT combinations since that is the Alt Gr >+ // key on some keyboards. See bug 20953. >+ ignore = (event.stateMask ^ SWT.ALT) == 0 || >+ (event.stateMask ^ SWT.CTRL) == 0 || >+ (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 || >+ (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0; >+ } >+ // -ignore anything below SPACE except for line delimiter keys and tab. >+ // -ignore DEL >+ if (!ignore && event.character > 31 && event.character != SWT.DEL || >+ event.character == SWT.CR || event.character == SWT.LF || >+ event.character == TAB) { >+ doContent(event.character); >+ } >+ } >+ else { >+ invokeAction(action); >+ } > } > /** > * If a VerifyKey listener exists, verify that the key that was entered >@@ -5106,66 +5116,71 @@ > * @param event keyboard event > */ > void handleKeyDown(Event event) { >- Event verifyEvent = new Event(); >- >- verifyEvent.character = event.character; >- verifyEvent.keyCode = event.keyCode; >- verifyEvent.stateMask = event.stateMask; >- verifyEvent.doit = true; >- notifyListeners(VerifyKey, verifyEvent); >- if (verifyEvent.doit == true) { >- handleKey(event); >- } >+ Event verifyEvent = new Event(); >+ >+ verifyEvent.character = event.character; >+ verifyEvent.keyCode = event.keyCode; >+ verifyEvent.stateMask = event.stateMask; >+ verifyEvent.doit = true; >+ notifyListeners(VerifyKey, verifyEvent); >+ if (verifyEvent.doit == true) { >+ handleKey(event); >+ } > } > /** >- * Updates the caret location and selection if mouse button 1 has been >+ * Updates the caret location and selection if mouse button 1 has been > * pressed. > */ > void handleMouseDoubleClick(Event event) { >- if (event.button != 1 || doubleClickEnabled == false) { >- return; >- } >- event.y -= topMargin; >- mouseDoubleClick = true; >- caretOffset = getWordStart(caretOffset); >- resetSelection(); >- caretOffset = getWordEndNoSpaces(caretOffset); >- showCaret(); >- doMouseSelection(); >- doubleClickSelection = new Point(selection.x, selection.y); >+ if (event.button != 1 || doubleClickEnabled == false) { >+ return; >+ } >+ event.y -= topMargin; >+ mouseDoubleClick = true; >+ caretOffset = getWordStart(caretOffset); >+ resetSelection(); >+ caretOffset = getWordEndNoSpaces(caretOffset); >+ showCaret(); >+ doMouseSelection(); >+ doubleClickSelection = new Point(selection.x, selection.y); > } >-/** >- * Updates the caret location and selection if mouse button 1 has been >+/** >+ * Updates the caret location and selection if mouse button 1 has been > * pressed. > */ > void handleMouseDown(Event event) { >- boolean select = (event.stateMask & SWT.MOD2) != 0; >- >- if (event.button != 1) { >- return; >- } >- mouseDoubleClick = false; >- event.y -= topMargin; >- doMouseLocationChange(event.x, event.y, select); >+ boolean select = (event.stateMask & SWT.MOD2) != 0; >+ >+ if ((event.button == 1) || (event.button == 3)) { >+ if ((event.button == 3) && (isMouseOverSelection(event.x, event.y))) { >+ // we have a right click over a selection, so don't alter the selection >+ return; >+ } >+ mouseDoubleClick = false; >+ event.y -= topMargin; >+ doMouseLocationChange(event.x, event.y, select); >+ } > } >-/** >- * Updates the caret location and selection if mouse button 1 is pressed >+/** >+ * Updates the caret location and selection if mouse button 1 is pressed > * during the mouse move. > */ > void handleMouseMove(Event event) { >- if ((event.stateMask & SWT.BUTTON1) == 0) { >- return; >- } >- event.y -= topMargin; >- doMouseLocationChange(event.x, event.y, true); >- doAutoScroll(event); >+ if ((event.stateMask & SWT.BUTTON1) != 0) { >+ event.y -= topMargin; >+ doMouseLocationChange(event.x, event.y, true); >+ doAutoScroll(event); >+ } else if (event.stateMask == 0) { >+ // changes the mouse pointer based on whether or not it is over a selection >+ internalSetCursor(event.x, event.y); >+ } > } >-/** >+/** > * Autoscrolling ends when the mouse button is released. > */ > void handleMouseUp(Event event) { >- event.y -= topMargin; >- endAutoScroll(); >+ event.y -= topMargin; >+ endAutoScroll(); > } > /** > * Renders the invalidated area specified in the paint event. >@@ -5174,204 +5189,204 @@ > * @param event paint event > */ > void handlePaint(Event event) { >- int startLine = Math.max(0, (event.y - topMargin + verticalScrollOffset) / lineHeight); >- int paintYFromTopLine = (startLine - topIndex) * lineHeight; >- int topLineOffset = topIndex * lineHeight - verticalScrollOffset; >- int startY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling and top margin >- int renderHeight = event.y + event.height - startY; >- Rectangle clientArea = getClientArea(); >- >- // Check if there is work to do. clientArea.width should never be 0 >- // if we receive a paint event but we never want to try and create >- // an Image with 0 width. >- if (clientArea.width == 0 || event.height == 0) { >- return; >- } >- performPaint(event.gc, startLine, startY, renderHeight); >-} >+ int startLine = Math.max(0, (event.y - topMargin + verticalScrollOffset) / lineHeight); >+ int paintYFromTopLine = (startLine - topIndex) * lineHeight; >+ int topLineOffset = topIndex * lineHeight - verticalScrollOffset; >+ int startY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling and top margin >+ int renderHeight = event.y + event.height - startY; >+ Rectangle clientArea = getClientArea(); >+ >+ // Check if there is work to do. clientArea.width should never be 0 >+ // if we receive a paint event but we never want to try and create >+ // an Image with 0 width. >+ if (clientArea.width == 0 || event.height == 0) { >+ return; >+ } >+ performPaint(event.gc, startLine, startY, renderHeight); >+} > /** >- * Recalculates the scroll bars. Rewraps all lines when in word >+ * Recalculates the scroll bars. Rewraps all lines when in word > * wrap mode. > * <p> > * > * @param event resize event > */ > void handleResize(Event event) { >- int oldHeight = clientAreaHeight; >- int oldWidth = clientAreaWidth; >- >- clientAreaHeight = getClientArea().height; >- clientAreaWidth = getClientArea().width; >- if (wordWrap) { >- if (oldWidth != clientAreaWidth) { >- wordWrapResize(oldWidth); >- } >- } >- else >- if (clientAreaHeight > oldHeight) { >- int lineCount = content.getLineCount(); >- int oldBottomIndex = topIndex + oldHeight / lineHeight; >- int newItemCount = Compatibility.ceil(clientAreaHeight - oldHeight, lineHeight); >- >- oldBottomIndex = Math.min(oldBottomIndex, lineCount); >- newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex); >- lineCache.calculate(oldBottomIndex, newItemCount); >- } >- setScrollBars(); >- claimBottomFreeSpace(); >- claimRightFreeSpace(); >- if (oldHeight != clientAreaHeight) { >- calculateTopIndex(); >- } >+ int oldHeight = clientAreaHeight; >+ int oldWidth = clientAreaWidth; >+ >+ clientAreaHeight = getClientArea().height; >+ clientAreaWidth = getClientArea().width; >+ if (wordWrap) { >+ if (oldWidth != clientAreaWidth) { >+ wordWrapResize(oldWidth); >+ } >+ } >+ else >+ if (clientAreaHeight > oldHeight) { >+ int lineCount = content.getLineCount(); >+ int oldBottomIndex = topIndex + oldHeight / lineHeight; >+ int newItemCount = Compatibility.ceil(clientAreaHeight - oldHeight, lineHeight); >+ >+ oldBottomIndex = Math.min(oldBottomIndex, lineCount); >+ newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex); >+ lineCache.calculate(oldBottomIndex, newItemCount); >+ } >+ setScrollBars(); >+ claimBottomFreeSpace(); >+ claimRightFreeSpace(); >+ if (oldHeight != clientAreaHeight) { >+ calculateTopIndex(); >+ } > } > /** >- * Updates the caret position and selection and the scroll bars to reflect >+ * Updates the caret position and selection and the scroll bars to reflect > * the content change. > * <p> > */ > void handleTextChanged(TextChangedEvent event) { >- lineCache.textChanged(lastTextChangeStart, >- lastTextChangeNewLineCount, >- lastTextChangeReplaceLineCount, >- lastTextChangeNewCharCount, >- lastTextChangeReplaceCharCount); >- setScrollBars(); >- // update selection/caret location after styles have been changed. >- // otherwise any text measuring could be incorrect >- // >- // also, this needs to be done after all scrolling. Otherwise, >- // selection redraw would be flushed during scroll which is wrong. >- // in some cases new text would be drawn in scroll source area even >- // though the intent is to scroll it. >- // fixes 1GB93QT >- updateSelection( >- lastTextChangeStart, >- lastTextChangeReplaceCharCount, >- lastTextChangeNewCharCount); >- >- if (lastTextChangeReplaceLineCount > 0) { >- // Only check for unused space when lines are deleted. >- // Fixes 1GFL4LY >- // Scroll up so that empty lines below last text line are used. >- // Fixes 1GEYJM0 >- claimBottomFreeSpace(); >- } >- if (lastTextChangeReplaceCharCount > 0) { >- // fixes bug 8273 >- claimRightFreeSpace(); >- } >- // do direct drawing if the text change is confined to a single line. >- // optimization and fixes bug 13999. see also handleTextChanging. >- if (lastTextChangeNewLineCount == 0 && lastTextChangeReplaceLineCount == 0) { >- int startLine = content.getLineAtOffset(lastTextChangeStart); >- int startY = startLine * lineHeight - verticalScrollOffset + topMargin; >- >- GC gc = getGC(); >- Caret caret = getCaret(); >- boolean caretVisible = false; >- >- if (caret != null) { >- caretVisible = caret.getVisible(); >- caret.setVisible(false); >- } >- performPaint(gc, startLine, startY, lineHeight); >- if (caret != null) { >- caret.setVisible(caretVisible); >- } >- gc.dispose(); >- } >+ lineCache.textChanged(lastTextChangeStart, >+ lastTextChangeNewLineCount, >+ lastTextChangeReplaceLineCount, >+ lastTextChangeNewCharCount, >+ lastTextChangeReplaceCharCount); >+ setScrollBars(); >+ // update selection/caret location after styles have been changed. >+ // otherwise any text measuring could be incorrect >+ // >+ // also, this needs to be done after all scrolling. Otherwise, >+ // selection redraw would be flushed during scroll which is wrong. >+ // in some cases new text would be drawn in scroll source area even >+ // though the intent is to scroll it. >+ // fixes 1GB93QT >+ updateSelection( >+ lastTextChangeStart, >+ lastTextChangeReplaceCharCount, >+ lastTextChangeNewCharCount); >+ >+ if (lastTextChangeReplaceLineCount > 0) { >+ // Only check for unused space when lines are deleted. >+ // Fixes 1GFL4LY >+ // Scroll up so that empty lines below last text line are used. >+ // Fixes 1GEYJM0 >+ claimBottomFreeSpace(); >+ } >+ if (lastTextChangeReplaceCharCount > 0) { >+ // fixes bug 8273 >+ claimRightFreeSpace(); >+ } >+ // do direct drawing if the text change is confined to a single line. >+ // optimization and fixes bug 13999. see also handleTextChanging. >+ if (lastTextChangeNewLineCount == 0 && lastTextChangeReplaceLineCount == 0) { >+ int startLine = content.getLineAtOffset(lastTextChangeStart); >+ int startY = startLine * lineHeight - verticalScrollOffset + topMargin; >+ >+ GC gc = getGC(); >+ Caret caret = getCaret(); >+ boolean caretVisible = false; >+ >+ if (caret != null) { >+ caretVisible = caret.getVisible(); >+ caret.setVisible(false); >+ } >+ performPaint(gc, startLine, startY, lineHeight); >+ if (caret != null) { >+ caret.setVisible(caretVisible); >+ } >+ gc.dispose(); >+ } > } > /** > * Updates the screen to reflect a pending content change. > * <p> > * > * @param event.start the start offset of the change >- * @param event.newText text that is going to be inserted or empty String >- * if no text will be inserted >+ * @param event.newText text that is going to be inserted or empty String >+ * if no text will be inserted > * @param event.replaceCharCount length of text that is going to be replaced > * @param event.newCharCount length of text that is going to be inserted > * @param event.replaceLineCount number of lines that are going to be replaced > * @param event.newLineCount number of new lines that are going to be inserted > */ > void handleTextChanging(TextChangingEvent event) { >- int firstLine; >- int textChangeY; >- boolean isMultiLineChange = event.replaceLineCount > 0 || event.newLineCount > 0; >- >- if (event.replaceCharCount < 0) { >- event.start += event.replaceCharCount; >- event.replaceCharCount *= -1; >- } >- lastTextChangeStart = event.start; >- lastTextChangeNewLineCount = event.newLineCount; >- lastTextChangeNewCharCount = event.newCharCount; >- lastTextChangeReplaceLineCount = event.replaceLineCount; >- lastTextChangeReplaceCharCount = event.replaceCharCount; >- firstLine = content.getLineAtOffset(event.start); >- textChangeY = firstLine * lineHeight - verticalScrollOffset + topMargin; >- if (isMultiLineChange) { >- redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount); >- } >- // notify default line styler about text change >- if (defaultLineStyler != null) { >- defaultLineStyler.textChanging(event); >- } >- >- // Update the caret offset if it is greater than the length of the content. >- // This is necessary since style range API may be called between the >- // handleTextChanging and handleTextChanged events and this API sets the >- // caretOffset. >- int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount; >- if (caretOffset > newEndOfText) caretOffset = newEndOfText; >+ int firstLine; >+ int textChangeY; >+ boolean isMultiLineChange = event.replaceLineCount > 0 || event.newLineCount > 0; >+ >+ if (event.replaceCharCount < 0) { >+ event.start += event.replaceCharCount; >+ event.replaceCharCount *= -1; >+ } >+ lastTextChangeStart = event.start; >+ lastTextChangeNewLineCount = event.newLineCount; >+ lastTextChangeNewCharCount = event.newCharCount; >+ lastTextChangeReplaceLineCount = event.replaceLineCount; >+ lastTextChangeReplaceCharCount = event.replaceCharCount; >+ firstLine = content.getLineAtOffset(event.start); >+ textChangeY = firstLine * lineHeight - verticalScrollOffset + topMargin; >+ if (isMultiLineChange) { >+ redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount); >+ } >+ // notify default line styler about text change >+ if (defaultLineStyler != null) { >+ defaultLineStyler.textChanging(event); >+ } >+ >+ // Update the caret offset if it is greater than the length of the content. >+ // This is necessary since style range API may be called between the >+ // handleTextChanging and handleTextChanged events and this API sets the >+ // caretOffset. >+ int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount; >+ if (caretOffset > newEndOfText) caretOffset = newEndOfText; > } > /** >- * Called when the widget content is set programatically, overwriting >- * the old content. Resets the caret position, selection and scroll offsets. >+ * Called when the widget content is set programatically, overwriting >+ * the old content. Resets the caret position, selection and scroll offsets. > * Recalculates the content width and scroll bars. Redraws the widget. > * <p> > * >- * @param event text change event. >+ * @param event text change event. > */ > void handleTextSet(TextChangedEvent event) { >- reset(); >+ reset(); > } > /** > * Called when a traversal key is pressed. >- * Allow tab next traversal to occur when the widget is in single >- * line mode or in multi line and non-editable mode . >- * When in editable multi line mode we want to prevent the tab >+ * Allow tab next traversal to occur when the widget is in single >+ * line mode or in multi line and non-editable mode . >+ * When in editable multi line mode we want to prevent the tab > * traversal and receive the tab key event instead. > * <p> > * > * @param event the event > */ > void handleTraverse(Event event) { >- int style = getStyle(); >- boolean ignoreTab = (style & SWT.MULTI) != 0 && !editable || isSingleLine(); >- >- if ((event.detail == SWT.TRAVERSE_TAB_NEXT || >- event.detail == SWT.TRAVERSE_RETURN) && ignoreTab) { >- event.doit = true; >- } >+ int style = getStyle(); >+ boolean ignoreTab = (style & SWT.MULTI) != 0 && !editable || isSingleLine(); >+ >+ if ((event.detail == SWT.TRAVERSE_TAB_NEXT || >+ event.detail == SWT.TRAVERSE_RETURN) && ignoreTab) { >+ event.doit = true; >+ } > } >-/** >+/** > * Scrolls the widget vertically. > */ > void handleVerticalScroll(Event event) { >- setVerticalScrollOffset(getVerticalBar().getSelection(), false); >+ setVerticalScrollOffset(getVerticalBar().getSelection(), false); > } >-/** >+/** > * Initializes the fonts used to render font styles. > * Presently only regular and bold fonts are supported. > */ > void initializeRenderer() { >- if (renderer != null) { >- renderer.dispose(); >- } >- renderer = new DisplayRenderer( >- getDisplay(), getFont(), isBidi(), leftMargin, this, tabLength); >- lineHeight = renderer.getLineHeight(); >- lineEndSpaceWidth = renderer.getLineEndSpaceWidth(); >+ if (renderer != null) { >+ renderer.dispose(); >+ } >+ renderer = new DisplayRenderer( >+ getDisplay(), getFont(), isBidi(), leftMargin, this, tabLength); >+ lineHeight = renderer.getLineHeight(); >+ lineEndSpaceWidth = renderer.getLineEndSpaceWidth(); > } > /** > * Executes the action. >@@ -5380,178 +5395,178 @@ > * @param action one of the actions defined in ST.java > */ > public void invokeAction(int action) { >- int oldColumnX; >- int caretLine; >- >- checkWidget(); >- switch (action) { >- // Navigation >- case ST.LINE_UP: >- caretLine = doLineUp(); >- oldColumnX = columnX; >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- // save the original horizontal caret position >- columnX = oldColumnX; >- clearSelection(true); >- break; >- case ST.LINE_DOWN: >- caretLine = doLineDown(); >- oldColumnX = columnX; >- // explicitly go to the calculated caret line. may be different >- // from content.getLineAtOffset(caretOffset) when in word wrap mode >- showCaret(caretLine); >- // save the original horizontal caret position >- columnX = oldColumnX; >- clearSelection(true); >- break; >- case ST.LINE_START: >- doLineStart(); >- clearSelection(true); >- break; >- case ST.LINE_END: >- doLineEnd(); >- clearSelection(true); >- break; >- case ST.COLUMN_PREVIOUS: >- doCursorPrevious(); >- clearSelection(true); >- break; >- case ST.COLUMN_NEXT: >- doCursorNext(); >- clearSelection(true); >- break; >- case ST.PAGE_UP: >- doPageUp(); >- clearSelection(true); >- break; >- case ST.PAGE_DOWN: >- doPageDown(false); >- clearSelection(true); >- break; >- case ST.WORD_PREVIOUS: >- doWordPrevious(); >- clearSelection(true); >- break; >- case ST.WORD_NEXT: >- doWordNext(); >- clearSelection(true); >- break; >- case ST.TEXT_START: >- doContentStart(); >- clearSelection(true); >- break; >- case ST.TEXT_END: >- doContentEnd(); >- clearSelection(true); >- break; >- case ST.WINDOW_START: >- doPageStart(); >- clearSelection(true); >- break; >- case ST.WINDOW_END: >- doPageEnd(); >- clearSelection(true); >- break; >- // Selection >- case ST.SELECT_LINE_UP: >- doSelectionLineUp(); >- break; >- case ST.SELECT_LINE_DOWN: >- doSelectionLineDown(); >- break; >- case ST.SELECT_LINE_START: >- doLineStart(); >- doSelection(SWT.LEFT); >- break; >- case ST.SELECT_LINE_END: >- doLineEnd(); >- doSelection(SWT.RIGHT); >- break; >- case ST.SELECT_COLUMN_PREVIOUS: >- doSelectionCursorPrevious(); >- doSelection(SWT.LEFT); >- break; >- case ST.SELECT_COLUMN_NEXT: >- doSelectionCursorNext(); >- doSelection(SWT.RIGHT); >- break; >- case ST.SELECT_PAGE_UP: >- doPageUp(); >- doSelection(SWT.LEFT); >- break; >- case ST.SELECT_PAGE_DOWN: >- doPageDown(true); >- break; >- case ST.SELECT_WORD_PREVIOUS: >- doSelectionWordPrevious(); >- doSelection(SWT.LEFT); >- break; >- case ST.SELECT_WORD_NEXT: >- doSelectionWordNext(); >- doSelection(SWT.RIGHT); >- break; >- case ST.SELECT_TEXT_START: >- doContentStart(); >- doSelection(SWT.LEFT); >- break; >- case ST.SELECT_TEXT_END: >- doContentEnd(); >- doSelection(SWT.RIGHT); >- break; >- case ST.SELECT_WINDOW_START: >- doPageStart(); >- doSelection(SWT.LEFT); >- break; >- case ST.SELECT_WINDOW_END: >- doPageEnd(); >- doSelection(SWT.RIGHT); >- break; >- // Modification >- case ST.CUT: >- cut(); >- break; >- case ST.COPY: >- copy(); >- break; >- case ST.PASTE: >- paste(); >- break; >- case ST.DELETE_PREVIOUS: >- doBackspace(); >- break; >- case ST.DELETE_NEXT: >- doDelete(); >- break; >- // Miscellaneous >- case ST.TOGGLE_OVERWRITE: >- overwrite = !overwrite; // toggle insert/overwrite mode >- break; >- } >+ int oldColumnX; >+ int caretLine; >+ >+ checkWidget(); >+ switch (action) { >+ // Navigation >+ case ST.LINE_UP: >+ caretLine = doLineUp(); >+ oldColumnX = columnX; >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ // save the original horizontal caret position >+ columnX = oldColumnX; >+ clearSelection(true); >+ break; >+ case ST.LINE_DOWN: >+ caretLine = doLineDown(); >+ oldColumnX = columnX; >+ // explicitly go to the calculated caret line. may be different >+ // from content.getLineAtOffset(caretOffset) when in word wrap mode >+ showCaret(caretLine); >+ // save the original horizontal caret position >+ columnX = oldColumnX; >+ clearSelection(true); >+ break; >+ case ST.LINE_START: >+ doLineStart(); >+ clearSelection(true); >+ break; >+ case ST.LINE_END: >+ doLineEnd(); >+ clearSelection(true); >+ break; >+ case ST.COLUMN_PREVIOUS: >+ doCursorPrevious(); >+ clearSelection(true); >+ break; >+ case ST.COLUMN_NEXT: >+ doCursorNext(); >+ clearSelection(true); >+ break; >+ case ST.PAGE_UP: >+ doPageUp(); >+ clearSelection(true); >+ break; >+ case ST.PAGE_DOWN: >+ doPageDown(false); >+ clearSelection(true); >+ break; >+ case ST.WORD_PREVIOUS: >+ doWordPrevious(); >+ clearSelection(true); >+ break; >+ case ST.WORD_NEXT: >+ doWordNext(); >+ clearSelection(true); >+ break; >+ case ST.TEXT_START: >+ doContentStart(); >+ clearSelection(true); >+ break; >+ case ST.TEXT_END: >+ doContentEnd(); >+ clearSelection(true); >+ break; >+ case ST.WINDOW_START: >+ doPageStart(); >+ clearSelection(true); >+ break; >+ case ST.WINDOW_END: >+ doPageEnd(); >+ clearSelection(true); >+ break; >+ // Selection >+ case ST.SELECT_LINE_UP: >+ doSelectionLineUp(); >+ break; >+ case ST.SELECT_LINE_DOWN: >+ doSelectionLineDown(); >+ break; >+ case ST.SELECT_LINE_START: >+ doLineStart(); >+ doSelection(SWT.LEFT); >+ break; >+ case ST.SELECT_LINE_END: >+ doLineEnd(); >+ doSelection(SWT.RIGHT); >+ break; >+ case ST.SELECT_COLUMN_PREVIOUS: >+ doSelectionCursorPrevious(); >+ doSelection(SWT.LEFT); >+ break; >+ case ST.SELECT_COLUMN_NEXT: >+ doSelectionCursorNext(); >+ doSelection(SWT.RIGHT); >+ break; >+ case ST.SELECT_PAGE_UP: >+ doPageUp(); >+ doSelection(SWT.LEFT); >+ break; >+ case ST.SELECT_PAGE_DOWN: >+ doPageDown(true); >+ break; >+ case ST.SELECT_WORD_PREVIOUS: >+ doSelectionWordPrevious(); >+ doSelection(SWT.LEFT); >+ break; >+ case ST.SELECT_WORD_NEXT: >+ doSelectionWordNext(); >+ doSelection(SWT.RIGHT); >+ break; >+ case ST.SELECT_TEXT_START: >+ doContentStart(); >+ doSelection(SWT.LEFT); >+ break; >+ case ST.SELECT_TEXT_END: >+ doContentEnd(); >+ doSelection(SWT.RIGHT); >+ break; >+ case ST.SELECT_WINDOW_START: >+ doPageStart(); >+ doSelection(SWT.LEFT); >+ break; >+ case ST.SELECT_WINDOW_END: >+ doPageEnd(); >+ doSelection(SWT.RIGHT); >+ break; >+ // Modification >+ case ST.CUT: >+ cut(); >+ break; >+ case ST.COPY: >+ copy(); >+ break; >+ case ST.PASTE: >+ paste(); >+ break; >+ case ST.DELETE_PREVIOUS: >+ doBackspace(); >+ break; >+ case ST.DELETE_NEXT: >+ doDelete(); >+ break; >+ // Miscellaneous >+ case ST.TOGGLE_OVERWRITE: >+ overwrite = !overwrite; // toggle insert/overwrite mode >+ break; >+ } > } > /** > * Temporary until SWT provides this > */ > boolean isBidi() { >- return isBidi; >+ return isBidi; > } > /** > * Returns whether the given offset is inside a multi byte line delimiter. >- * Example: >+ * Example: > * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true >- * >+ * > * @return true if the given offset is inside a multi byte line delimiter. > * false if the given offset is before or after a line delimiter. > */ > boolean isLineDelimiter(int offset) { >- int line = content.getLineAtOffset(offset); >- int lineOffset = content.getOffsetAtLine(line); >- int offsetInLine = offset - lineOffset; >- // offsetInLine will be greater than line length if the line >- // delimiter is longer than one character and the offset is set >- // in between parts of the line delimiter. >- return offsetInLine > content.getLine(line).length(); >+ int line = content.getLineAtOffset(offset); >+ int lineOffset = content.getOffsetAtLine(line); >+ int offsetInLine = offset - lineOffset; >+ // offsetInLine will be greater than line length if the line >+ // delimiter is longer than one character and the offset is set >+ // in between parts of the line delimiter. >+ return offsetInLine > content.getLine(line).length(); > } > /** > * Returns whether or not the given lines are visible. >@@ -5561,190 +5576,243 @@ > * false if none of the lines is visible > */ > boolean isAreaVisible(int firstLine, int lastLine) { >- int partialBottomIndex = getPartialBottomIndex(); >- int partialTopIndex = verticalScrollOffset / lineHeight; >- boolean notVisible = firstLine > partialBottomIndex || lastLine < partialTopIndex; >- return !notVisible; >+ int partialBottomIndex = getPartialBottomIndex(); >+ int partialTopIndex = verticalScrollOffset / lineHeight; >+ boolean notVisible = firstLine > partialBottomIndex || lastLine < partialTopIndex; >+ return !notVisible; >+} >+/** >+ * Tests whether the mouse pointer is over a selection. >+ * >+ * @param x mouse x position in client coordinates >+ * @param y mouse y position in client coordinates >+ */ >+private boolean isMouseOverSelection(int x, int y) { >+ if (selectionAnchor == -1) { >+ // no selection >+ return false; >+ } else { >+ // there is a selection >+ int mouseLineIndex = (y + verticalScrollOffset) / lineHeight; >+ int mouseCharOffset; >+ int selLineIndexX; >+ int selLineIndexY; >+ int selLineOffset; >+ String lineContent = null; >+ >+ selLineIndexX = content.getLineAtOffset(selection.x); >+ selLineIndexY = content.getLineAtOffset(selection.y); >+ >+ if ((mouseLineIndex < selLineIndexX) || (mouseLineIndex > selLineIndexY)) { >+ // mouse is before the first or after the last line of the selection >+ return false; >+ } >+ >+ if (mouseLineIndex == selLineIndexX) { >+ // the mouse is on the first line of the selection >+ selLineOffset = content.getOffsetAtLine(selLineIndexX); >+ lineContent = content.getLine(selLineIndexX); >+ if (x < getXAtOffset(lineContent, selLineIndexX, selection.x - selLineOffset)) { >+ // mouse is before the start of the selection >+ return false; >+ } >+ } >+ >+ if (mouseLineIndex == selLineIndexY) { >+ // the mouse is on the last line of the selection >+ selLineOffset = content.getOffsetAtLine(selLineIndexY); >+ if (selLineIndexY != selLineIndexX) { >+ lineContent = content.getLine(selLineIndexY); >+ } >+ if (x > getXAtOffset(lineContent, selLineIndexY, selection.y - selLineOffset)) { >+ // mouse is after the end of the selection >+ return false; >+ } >+ } >+ >+ // mouse is over the selection >+ return true; >+ } > } > /** >- * Returns whether or not the given styles will necessitate a redraw for the given start line. >- * A redraw is necessary when font style changes after the start of a style will take place. >+ * Returns whether or not the given styles will necessitate a redraw for the given start line. >+ * A redraw is necessary when font style changes after the start of a style will take place. > * This method assumes ranges is in order and non-overlapping. > * <p> > * > * @return true if a redraw of the given line is necessary, false otherwise > */ > boolean isRedrawFirstLine(StyleRange[] ranges, int firstLine, int firstLineOffset) { >- int lineEnd = firstLineOffset + content.getLine(firstLine).length(); >- for (int i=0; i<ranges.length; i++) { >- StyleRange range = ranges[i]; >- // does the style start on the first line? >- if (range.start < lineEnd) { >- int rangeEnd = range.start + range.length; >- if (isStyleChanging(range, range.start, Math.min(rangeEnd, lineEnd))) return true; >- } else { >- return false; >- } >- } >- return false; >+ int lineEnd = firstLineOffset + content.getLine(firstLine).length(); >+ for (int i=0; i<ranges.length; i++) { >+ StyleRange range = ranges[i]; >+ // does the style start on the first line? >+ if (range.start < lineEnd) { >+ int rangeEnd = range.start + range.length; >+ if (isStyleChanging(range, range.start, Math.min(rangeEnd, lineEnd))) return true; >+ } else { >+ return false; >+ } >+ } >+ return false; > } > /** >- * Returns whether or not the given styles will necessitate a redraw for the given end line. >- * A redraw is necessary when font style changes after the start of a style will take place. >+ * Returns whether or not the given styles will necessitate a redraw for the given end line. >+ * A redraw is necessary when font style changes after the start of a style will take place. > * This method assumes ranges is in order and non-overlapping. > * <p> > * > * @return true if a redraw of the last line is necessary, false otherwise > */ > boolean isRedrawLastLine(StyleRange[] ranges, int lastLine, int lastLineOffset) { >- for (int i = ranges.length - 1; i >= 0; i--) { >- StyleRange range = ranges[i]; >- int rangeEnd = range.start + range.length; >- // does style range end on the last line? >- if (rangeEnd >= lastLineOffset) { >- if (isStyleChanging(range, Math.max(range.start, lastLineOffset), rangeEnd)) return true; >- } else { >- break; >- } >- } >- return false; >+ for (int i = ranges.length - 1; i >= 0; i--) { >+ StyleRange range = ranges[i]; >+ int rangeEnd = range.start + range.length; >+ // does style range end on the last line? >+ if (rangeEnd >= lastLineOffset) { >+ if (isStyleChanging(range, Math.max(range.start, lastLineOffset), rangeEnd)) return true; >+ } else { >+ break; >+ } >+ } >+ return false; > } > /** > * Returns whether the widget can have only one line. > * <p> > * >- * @return true if widget can have only one line, false if widget can have >- * multiple lines >+ * @return true if widget can have only one line, false if widget can have >+ * multiple lines > */ > boolean isSingleLine() { >- return (getStyle() & SWT.SINGLE) != 0; >+ return (getStyle() & SWT.SINGLE) != 0; > } >-/** >- * Returns whether the font style in the given style range is changing >+/** >+ * Returns whether the font style in the given style range is changing > * from SWT.NORMAL to SWT.BOLD or vice versa. > * <p> > * > * @param range StyleRange to compare current font style with. >- * @param start offset of the first font style to compare >+ * @param start offset of the first font style to compare > * @param end offset behind the last font style to compare > * @return true if the font style is changing in the given style range, >- * false if the font style is not changing in the given style range. >+ * false if the font style is not changing in the given style range. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > boolean isStyleChanging(StyleRange range, int start, int end) { >- checkWidget(); >- StyleRange[] styles = defaultLineStyler.getStyleRangesFor(start, end - start); >- >- if (styles == null) { >- return (range.fontStyle != SWT.NORMAL); >- } >- for (int i = 0; i < styles.length; i++) { >- StyleRange newStyle = styles[i]; >- if (newStyle.fontStyle != range.fontStyle) { >- return true; >- } >- } >- return false; >+ checkWidget(); >+ StyleRange[] styles = defaultLineStyler.getStyleRangesFor(start, end - start); >+ >+ if (styles == null) { >+ return (range.fontStyle != SWT.NORMAL); >+ } >+ for (int i = 0; i < styles.length; i++) { >+ StyleRange newStyle = styles[i]; >+ if (newStyle.fontStyle != range.fontStyle) { >+ return true; >+ } >+ } >+ return false; > } > /** >- * Sends the specified verify event, replace/insert text as defined by >+ * Sends the specified verify event, replace/insert text as defined by > * the event and send a modify event. > * <p> > * >- * @param event the text change event. >- * <ul> >- * <li>event.start - the replace start offset</li> >- * <li>event.end - the replace end offset</li> >- * <li>event.text - the new text</li> >- * </ul> >+ * @param event the text change event. >+ * <ul> >+ * <li>event.start - the replace start offset</li> >+ * <li>event.end - the replace end offset</li> >+ * <li>event.text - the new text</li> >+ * </ul> > * @param updateCaret whether or not he caret should be set behind >- * the new text >+ * the new text > */ > void modifyContent(Event event, boolean updateCaret) { >- event.doit = true; >- notifyListeners(SWT.Verify, event); >- if (event.doit) { >- StyledTextEvent styledTextEvent = null; >- int replacedLength = event.end - event.start; >- boolean isCharacterRemove = replacedLength == 1 && event.text.length() == 0; >- boolean isBackspace = event.start < caretOffset; >- boolean isDirectionBoundary = false; >- >- if (updateCaret && isBidi() && isCharacterRemove) { >- // set the keyboard language to the language of the deleted character. >- // determine direction boundary so that caret location can be updated >- // properly. >- int line = content.getLineAtOffset(caretOffset); >- int lineStartOffset = content.getOffsetAtLine(line); >- int offsetInLine = caretOffset - lineStartOffset; >- String lineText = content.getLine(line); >- GC gc = getGC(); >- StyledTextBidi bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText)); >- if (isBackspace) { >- if (offsetInLine > 0) { >- // the line start/end does not represent a direction boundary >- // even if the previous/next line has a different direction. >- isDirectionBoundary = >- offsetInLine < lineText.length() && >- (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) || >- bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1)); >- bidi.setKeyboardLanguage(offsetInLine - 1); >- } >- } >- else { >- if (offsetInLine < lineText.length()) { >- // the line start/end does not represent a direction boundary >- // even if the previous/next line has a different direction. >- isDirectionBoundary = >- offsetInLine > 0 && >- (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) || >- bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1)); >- bidi.setKeyboardLanguage(offsetInLine); >- } >- } >- gc.dispose(); >- } >- if (isListening(ExtendedModify)) { >- styledTextEvent = new StyledTextEvent(logicalContent); >- styledTextEvent.start = event.start; >- styledTextEvent.end = event.start + event.text.length(); >- styledTextEvent.text = content.getTextRange(event.start, replacedLength); >- } >- content.replaceTextRange(event.start, replacedLength, event.text); >- // set the caret position prior to sending the modify event. >- // fixes 1GBB8NJ >- if (updateCaret) { >- // always update the caret location. fixes 1G8FODP >- internalSetSelection(event.start + event.text.length(), 0, true); >- if (isBidi()) { >- // Update the caret direction so that the caret moves to the >- // typed/deleted character. Fixes 1GJLQ16. >- if (isCharacterRemove) { >- updateBidiDirection(isBackspace, isDirectionBoundary); >- } >- else { >- lastCaretDirection = ST.COLUMN_NEXT; >- } >- showBidiCaret(); >- } >- else { >- showCaret(); >- } >- } >- notifyListeners(SWT.Modify, event); >- if (isListening(ExtendedModify)) { >- notifyListeners(ExtendedModify, styledTextEvent); >- } >- } >-} >-/** >- * Replaces the selection with the clipboard text or insert the text at >- * the current caret offset if there is no selection. >+ event.doit = true; >+ notifyListeners(SWT.Verify, event); >+ if (event.doit) { >+ StyledTextEvent styledTextEvent = null; >+ int replacedLength = event.end - event.start; >+ boolean isCharacterRemove = replacedLength == 1 && event.text.length() == 0; >+ boolean isBackspace = event.start < caretOffset; >+ boolean isDirectionBoundary = false; >+ >+ if (updateCaret && isBidi() && isCharacterRemove) { >+ // set the keyboard language to the language of the deleted character. >+ // determine direction boundary so that caret location can be updated >+ // properly. >+ int line = content.getLineAtOffset(caretOffset); >+ int lineStartOffset = content.getOffsetAtLine(line); >+ int offsetInLine = caretOffset - lineStartOffset; >+ String lineText = content.getLine(line); >+ GC gc = getGC(); >+ StyledTextBidi bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText)); >+ if (isBackspace) { >+ if (offsetInLine > 0) { >+ // the line start/end does not represent a direction boundary >+ // even if the previous/next line has a different direction. >+ isDirectionBoundary = >+ offsetInLine < lineText.length() && >+ (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) || >+ bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1)); >+ bidi.setKeyboardLanguage(offsetInLine - 1); >+ } >+ } >+ else { >+ if (offsetInLine < lineText.length()) { >+ // the line start/end does not represent a direction boundary >+ // even if the previous/next line has a different direction. >+ isDirectionBoundary = >+ offsetInLine > 0 && >+ (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) || >+ bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1)); >+ bidi.setKeyboardLanguage(offsetInLine); >+ } >+ } >+ gc.dispose(); >+ } >+ if (isListening(ExtendedModify)) { >+ styledTextEvent = new StyledTextEvent(logicalContent); >+ styledTextEvent.start = event.start; >+ styledTextEvent.end = event.start + event.text.length(); >+ styledTextEvent.text = content.getTextRange(event.start, replacedLength); >+ } >+ content.replaceTextRange(event.start, replacedLength, event.text); >+ // set the caret position prior to sending the modify event. >+ // fixes 1GBB8NJ >+ if (updateCaret) { >+ // always update the caret location. fixes 1G8FODP >+ internalSetSelection(event.start + event.text.length(), 0, true); >+ if (isBidi()) { >+ // Update the caret direction so that the caret moves to the >+ // typed/deleted character. Fixes 1GJLQ16. >+ if (isCharacterRemove) { >+ updateBidiDirection(isBackspace, isDirectionBoundary); >+ } >+ else { >+ lastCaretDirection = ST.COLUMN_NEXT; >+ } >+ showBidiCaret(); >+ } >+ else { >+ showCaret(); >+ } >+ } >+ notifyListeners(SWT.Modify, event); >+ if (isListening(ExtendedModify)) { >+ notifyListeners(ExtendedModify, styledTextEvent); >+ } >+ } >+} >+/** >+ * Replaces the selection with the clipboard text or insert the text at >+ * the current caret offset if there is no selection. > * If the widget has the SWT.SINGLE style and the clipboard text contains >- * more than one line, only the first line without line delimiters is >+ * more than one line, only the first line without line delimiters is > * inserted in the widget. > * <p> > * >@@ -5754,73 +5822,73 @@ > * </ul> > */ > public void paste(){ >- checkWidget(); >- TextTransfer transfer = TextTransfer.getInstance(); >- String text; >- text = (String) clipboard.getContents(transfer); >- if (text != null && text.length() > 0) { >- Event event = new Event(); >- event.start = selection.x; >- event.end = selection.y; >- event.text = getModelDelimitedText(text); >- sendKeyEvent(event); >- } >+ checkWidget(); >+ TextTransfer transfer = TextTransfer.getInstance(); >+ String text; >+ text = (String) clipboard.getContents(transfer); >+ if (text != null && text.length() > 0) { >+ Event event = new Event(); >+ event.start = selection.x; >+ event.end = selection.y; >+ event.text = getModelDelimitedText(text); >+ sendKeyEvent(event); >+ } > } > /** > * Render the specified area. Broken out as its own method to support > * direct drawing. > * <p> > * >- * @param gc GC to render on >+ * @param gc GC to render on > * @param startLine first line to render > * @param startY y pixel location to start rendering at > * @param renderHeight renderHeight widget area that needs to be filled with lines > */ >-void performPaint(GC gc,int startLine,int startY, int renderHeight) { >- Rectangle clientArea = getClientArea(); >- Color background = getBackground(); >- >- // Check if there is work to do. We never want to try and create >- // an Image with 0 width or 0 height. >- if (clientArea.width == 0) { >- return; >- } >- if (renderHeight > 0) { >- // renderHeight will be negative when only top margin needs redrawing >- Color foreground = getForeground(); >- int lineCount = content.getLineCount(); >- int paintY = 0; >- >- if (isSingleLine()) { >- lineCount = 1; >- if (startLine > 1) { >- startLine = 1; >- } >- } >- Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight); >- GC lineGC = new GC(lineBuffer); >- >- lineGC.setFont(getFont()); >- renderer.setCurrentFontStyle(SWT.NORMAL); >- lineGC.setForeground(foreground); >- lineGC.setBackground(background); >- >- for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) { >- String line = content.getLine(i); >- renderer.drawLine(line, i, paintY, lineGC, background, foreground, true); >- } >- if (paintY < renderHeight) { >- lineGC.setBackground(background); >- lineGC.setForeground(background); >- lineGC.fillRectangle(0, paintY, clientArea.width, renderHeight - paintY); >- } >- gc.drawImage(lineBuffer, 0, startY); >- lineGC.dispose(); >- lineBuffer.dispose(); >- } >- clearMargin(gc, background, clientArea, renderHeight); >+void performPaint(GC gc,int startLine,int startY, int renderHeight) { >+ Rectangle clientArea = getClientArea(); >+ Color background = getBackground(); >+ >+ // Check if there is work to do. We never want to try and create >+ // an Image with 0 width or 0 height. >+ if (clientArea.width == 0) { >+ return; >+ } >+ if (renderHeight > 0) { >+ // renderHeight will be negative when only top margin needs redrawing >+ Color foreground = getForeground(); >+ int lineCount = content.getLineCount(); >+ int paintY = 0; >+ >+ if (isSingleLine()) { >+ lineCount = 1; >+ if (startLine > 1) { >+ startLine = 1; >+ } >+ } >+ Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight); >+ GC lineGC = new GC(lineBuffer); >+ >+ lineGC.setFont(getFont()); >+ renderer.setCurrentFontStyle(SWT.NORMAL); >+ lineGC.setForeground(foreground); >+ lineGC.setBackground(background); >+ >+ for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) { >+ String line = content.getLine(i); >+ renderer.drawLine(line, i, paintY, lineGC, background, foreground, true); >+ } >+ if (paintY < renderHeight) { >+ lineGC.setBackground(background); >+ lineGC.setForeground(background); >+ lineGC.fillRectangle(0, paintY, clientArea.width, renderHeight - paintY); >+ } >+ gc.drawImage(lineBuffer, 0, startY); >+ lineGC.dispose(); >+ lineBuffer.dispose(); >+ } >+ clearMargin(gc, background, clientArea, renderHeight); > } >-/** >+/** > * Prints the widget's text to the default printer. > * > * @exception SWTException <ul> >@@ -5829,24 +5897,24 @@ > * </ul> > */ > public void print() { >- checkWidget(); >- Printer printer = new Printer(); >- StyledTextPrintOptions options = new StyledTextPrintOptions(); >- >- options.printTextForeground = true; >- options.printTextBackground = true; >- options.printTextFontStyle = true; >- options.printLineBackground = true; >- new Printing(this, printer, options).run(); >- printer.dispose(); >+ checkWidget(); >+ Printer printer = new Printer(); >+ StyledTextPrintOptions options = new StyledTextPrintOptions(); >+ >+ options.printTextForeground = true; >+ options.printTextBackground = true; >+ options.printTextFontStyle = true; >+ options.printLineBackground = true; >+ new Printing(this, printer, options).run(); >+ printer.dispose(); > } >-/** >+/** > * Returns a runnable that will print the widget's text > * to the specified printer. > * <p> > * The runnable may be run in a non-UI thread. > * </p> >- * >+ * > * @param printer the printer to print to > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -5857,25 +5925,25 @@ > * </ul> > */ > public Runnable print(Printer printer) { >- StyledTextPrintOptions options = new StyledTextPrintOptions(); >+ StyledTextPrintOptions options = new StyledTextPrintOptions(); > >- checkWidget(); >- options.printTextForeground = true; >- options.printTextBackground = true; >- options.printTextFontStyle = true; >- options.printLineBackground = true; >- if (printer == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- return print(printer, options); >+ checkWidget(); >+ options.printTextForeground = true; >+ options.printTextBackground = true; >+ options.printTextFontStyle = true; >+ options.printLineBackground = true; >+ if (printer == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ return print(printer, options); > } >-/** >+/** > * Returns a runnable that will print the widget's text > * to the specified printer. > * <p> > * The runnable may be run in a non-UI thread. > * </p> >- * >+ * > * @param printer the printer to print to > * @param options print options to use during printing > * @exception SWTException <ul> >@@ -5888,11 +5956,11 @@ > * @since 2.1 > */ > public Runnable print(Printer printer, StyledTextPrintOptions options) { >- checkWidget(); >- if (printer == null || options == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- return new Printing(this, printer, options); >+ checkWidget(); >+ if (printer == null || options == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ return new Printing(this, printer, options); > } > /** > * Causes the entire bounds of the receiver to be marked >@@ -5900,8 +5968,8 @@ > * is processed, the control will be completely painted. > * <p> > * Recalculates the content width for all lines in the bounds. >- * When a <code>LineStyleListener</code> is used a redraw call >- * is the only notification to the widget that styles have changed >+ * When a <code>LineStyleListener</code> is used a redraw call >+ * is the only notification to the widget that styles have changed > * and that the content width may have changed. > * </p> > * >@@ -5913,28 +5981,28 @@ > * @see Control#update > */ > public void redraw() { >- int itemCount; >- >- super.redraw(); >- itemCount = getPartialBottomIndex() - topIndex + 1; >- lineCache.redrawReset(topIndex, itemCount, true); >- lineCache.calculate(topIndex, itemCount); >- setHorizontalScrollBar(); >+ int itemCount; >+ >+ super.redraw(); >+ itemCount = getPartialBottomIndex() - topIndex + 1; >+ lineCache.redrawReset(topIndex, itemCount, true); >+ lineCache.calculate(topIndex, itemCount); >+ setHorizontalScrollBar(); > } > /** > * Causes the rectangular area of the receiver specified by >- * the arguments to be marked as needing to be redrawn. >+ * the arguments to be marked as needing to be redrawn. > * The next time a paint request is processed, that area of > * the receiver will be painted. If the <code>all</code> flag > * is <code>true</code>, any children of the receiver which > * intersect with the specified area will also paint their >- * intersecting areas. If the <code>all</code> flag is >+ * intersecting areas. If the <code>all</code> flag is > * <code>false</code>, the children will not be painted. > * <p> > * Marks the content width of all lines in the specified rectangle > * as unknown. Recalculates the content width of all visible lines. >- * When a <code>LineStyleListener</code> is used a redraw call >- * is the only notification to the widget that styles have changed >+ * When a <code>LineStyleListener</code> is used a redraw call >+ * is the only notification to the widget that styles have changed > * and that the content width may have changed. > * </p> > * >@@ -5952,24 +6020,24 @@ > * @see Control#update > */ > public void redraw(int x, int y, int width, int height, boolean all) { >- super.redraw(x, y, width, height, all); >- if (height > 0) { >- int lineCount = content.getLineCount(); >- int startLine = (getTopPixel() + y) / lineHeight; >- int endLine = startLine + Compatibility.ceil(height, lineHeight); >- int itemCount; >- >- // reset all lines in the redraw rectangle >- startLine = Math.min(startLine, lineCount); >- itemCount = Math.min(endLine, lineCount) - startLine; >- lineCache.reset(startLine, itemCount, true); >- // only calculate the visible lines >- itemCount = getPartialBottomIndex() - topIndex + 1; >- lineCache.calculate(topIndex, itemCount); >- setHorizontalScrollBar(); >- } >+ super.redraw(x, y, width, height, all); >+ if (height > 0) { >+ int lineCount = content.getLineCount(); >+ int startLine = (getTopPixel() + y) / lineHeight; >+ int endLine = startLine + Compatibility.ceil(height, lineHeight); >+ int itemCount; >+ >+ // reset all lines in the redraw rectangle >+ startLine = Math.min(startLine, lineCount); >+ itemCount = Math.min(endLine, lineCount) - startLine; >+ lineCache.reset(startLine, itemCount, true); >+ // only calculate the visible lines >+ itemCount = getPartialBottomIndex() - topIndex + 1; >+ lineCache.calculate(topIndex, itemCount); >+ setHorizontalScrollBar(); >+ } > } >-/** >+/** > * Redraws a text range in the specified lines > * <p> > * >@@ -5982,53 +6050,53 @@ > * without invalidating the redraw range. > */ > void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) { >- int lineCount = lastLine - firstLine + 1; >- int redrawY = firstLine * lineHeight - verticalScrollOffset; >- int firstLineOffset = content.getOffsetAtLine(firstLine); >- String line = content.getLine(firstLine); >- GC gc = getGC(); >- StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc); >- >- bidi.redrawRange( >- this, offsetInFirstLine, >- Math.min(line.length(), endOffset) - offsetInFirstLine, >- leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight); >- // redraw line break marker (either space or full client area width) >- // if redraw range extends over more than one line and background should be redrawn >- if (lastLine > firstLine && clearBackground) { >- int lineBreakWidth; >- int lineBreakStartX = bidi.getTextWidth(); >- // handle empty line case >- if (lineBreakStartX == leftMargin) { >- lineBreakStartX += XINSET; >- } >- lineBreakStartX = lineBreakStartX - horizontalScrollOffset; >- if ((getStyle() & SWT.FULL_SELECTION) != 0) { >- lineBreakWidth = getClientArea().width - lineBreakStartX; >- } >- else { >- lineBreakWidth = lineEndSpaceWidth; >- } >- draw(lineBreakStartX, redrawY, lineBreakWidth, lineHeight, clearBackground); >- } >- // redraw last line if more than one line needs redrawing >- if (lineCount > 1) { >- int lastLineOffset = content.getOffsetAtLine(lastLine); >- int offsetInLastLine = endOffset - lastLineOffset; >- // no redraw necessary if redraw offset is 0 >- if (offsetInLastLine > 0) { >- line = content.getLine(lastLine); >- redrawY = lastLine * lineHeight - verticalScrollOffset; >- bidi = getStyledTextBidi(line, lastLineOffset, gc); >- bidi.redrawRange( >- this, 0, offsetInLastLine, >- leftMargin - horizontalScrollOffset, >- redrawY + topMargin, lineHeight); >- } >- } >- gc.dispose(); >+ int lineCount = lastLine - firstLine + 1; >+ int redrawY = firstLine * lineHeight - verticalScrollOffset; >+ int firstLineOffset = content.getOffsetAtLine(firstLine); >+ String line = content.getLine(firstLine); >+ GC gc = getGC(); >+ StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc); >+ >+ bidi.redrawRange( >+ this, offsetInFirstLine, >+ Math.min(line.length(), endOffset) - offsetInFirstLine, >+ leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight); >+ // redraw line break marker (either space or full client area width) >+ // if redraw range extends over more than one line and background should be redrawn >+ if (lastLine > firstLine && clearBackground) { >+ int lineBreakWidth; >+ int lineBreakStartX = bidi.getTextWidth(); >+ // handle empty line case >+ if (lineBreakStartX == leftMargin) { >+ lineBreakStartX += XINSET; >+ } >+ lineBreakStartX = lineBreakStartX - horizontalScrollOffset; >+ if ((getStyle() & SWT.FULL_SELECTION) != 0) { >+ lineBreakWidth = getClientArea().width - lineBreakStartX; >+ } >+ else { >+ lineBreakWidth = lineEndSpaceWidth; >+ } >+ draw(lineBreakStartX, redrawY, lineBreakWidth, lineHeight, clearBackground); >+ } >+ // redraw last line if more than one line needs redrawing >+ if (lineCount > 1) { >+ int lastLineOffset = content.getOffsetAtLine(lastLine); >+ int offsetInLastLine = endOffset - lastLineOffset; >+ // no redraw necessary if redraw offset is 0 >+ if (offsetInLastLine > 0) { >+ line = content.getLine(lastLine); >+ redrawY = lastLine * lineHeight - verticalScrollOffset; >+ bidi = getStyledTextBidi(line, lastLineOffset, gc); >+ bidi.redrawRange( >+ this, 0, offsetInLastLine, >+ leftMargin - horizontalScrollOffset, >+ redrawY + topMargin, lineHeight); >+ } >+ } >+ gc.dispose(); > } >-/** >+/** > * Redraw the given line. > * <p> > * >@@ -6036,17 +6104,17 @@ > * @param offset offset in line to start redrawing > */ > void redrawLine(int line, int offset) { >- int redrawX = 0; >- if (offset > 0) { >- String lineText = content.getLine(line); >- redrawX = getXAtOffset(lineText, line, offset); >- } >- int redrawY = line * lineHeight - verticalScrollOffset; >- super.redraw( >- redrawX + leftMargin, redrawY + topMargin, >- getClientArea().width, lineHeight, true); >+ int redrawX = 0; >+ if (offset > 0) { >+ String lineText = content.getLine(line); >+ redrawX = getXAtOffset(lineText, line, offset); >+ } >+ int redrawY = line * lineHeight - verticalScrollOffset; >+ super.redraw( >+ redrawX + leftMargin, redrawY + topMargin, >+ getClientArea().width, lineHeight, true); > } >-/** >+/** > * Redraws a text range in the specified lines > * <p> > * >@@ -6055,55 +6123,55 @@ > * @param lastLine last line to redraw > * @param endOffset offset in the last where redrawing should stop > * @param clearBackground true=clear the background by invalidating >- * the requested redraw range. If the redraw range includes the >- * last character of a line (i.e., the entire line is redrawn) the >- * line is cleared all the way to the right border of the widget. >- * false=draw the foreground directly without invalidating the >- * redraw range. >+ * the requested redraw range. If the redraw range includes the >+ * last character of a line (i.e., the entire line is redrawn) the >+ * line is cleared all the way to the right border of the widget. >+ * false=draw the foreground directly without invalidating the >+ * redraw range. > */ > void redrawLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) { >- String line = content.getLine(firstLine); >- int lineCount = lastLine - firstLine + 1; >- int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine) - leftMargin; >- int redrawStopX; >- int redrawY = firstLine * lineHeight - verticalScrollOffset; >- int firstLineOffset = content.getOffsetAtLine(firstLine); >- boolean fullLineRedraw = ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine); >- >- // if redraw range includes last character on the first line, >- // clear background to right widget border. fixes bug 19595. >- if (clearBackground && endOffset - firstLineOffset >= line.length()) { >- fullLineRedraw = true; >- } >- // calculate redraw stop location >- if (fullLineRedraw) { >- redrawStopX = getClientArea().width - leftMargin; >- } >- else { >- redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset) - leftMargin; >- } >- draw(redrawX, redrawY, redrawStopX - redrawX, lineHeight, clearBackground); >- // redraw last line if more than one line needs redrawing >- if (lineCount > 1) { >- int offsetInLastLine = endOffset - content.getOffsetAtLine(lastLine); >- // no redraw necessary if redraw offset is 0 >- if (offsetInLastLine > 0) { >- line = content.getLine(lastLine); >- // if redraw range includes last character on the last line, >- // clear background to right widget border. fixes bug 19595. >- if (clearBackground && offsetInLastLine >= line.length()) { >- fullLineRedraw = true; >- } >- if (fullLineRedraw) { >- redrawStopX = getClientArea().width - leftMargin; >- } >- else { >- redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine) - leftMargin; >- } >- redrawY = lastLine * lineHeight - verticalScrollOffset; >- draw(0, redrawY, redrawStopX, lineHeight, clearBackground); >- } >- } >+ String line = content.getLine(firstLine); >+ int lineCount = lastLine - firstLine + 1; >+ int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine) - leftMargin; >+ int redrawStopX; >+ int redrawY = firstLine * lineHeight - verticalScrollOffset; >+ int firstLineOffset = content.getOffsetAtLine(firstLine); >+ boolean fullLineRedraw = ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine); >+ >+ // if redraw range includes last character on the first line, >+ // clear background to right widget border. fixes bug 19595. >+ if (clearBackground && endOffset - firstLineOffset >= line.length()) { >+ fullLineRedraw = true; >+ } >+ // calculate redraw stop location >+ if (fullLineRedraw) { >+ redrawStopX = getClientArea().width - leftMargin; >+ } >+ else { >+ redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset) - leftMargin; >+ } >+ draw(redrawX, redrawY, redrawStopX - redrawX, lineHeight, clearBackground); >+ // redraw last line if more than one line needs redrawing >+ if (lineCount > 1) { >+ int offsetInLastLine = endOffset - content.getOffsetAtLine(lastLine); >+ // no redraw necessary if redraw offset is 0 >+ if (offsetInLastLine > 0) { >+ line = content.getLine(lastLine); >+ // if redraw range includes last character on the last line, >+ // clear background to right widget border. fixes bug 19595. >+ if (clearBackground && offsetInLastLine >= line.length()) { >+ fullLineRedraw = true; >+ } >+ if (fullLineRedraw) { >+ redrawStopX = getClientArea().width - leftMargin; >+ } >+ else { >+ redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine) - leftMargin; >+ } >+ redrawY = lastLine * lineHeight - verticalScrollOffset; >+ draw(0, redrawY, redrawStopX, lineHeight, clearBackground); >+ } >+ } > } > /** > * Fixes the widget to display a text change. >@@ -6115,50 +6183,50 @@ > * @param replacedLineCount number of replaced lines. > */ > void redrawMultiLineChange(int y, int newLineCount, int replacedLineCount) { >- Rectangle clientArea = getClientArea(); >- int lineCount = newLineCount - replacedLineCount; >- int sourceY; >- int destinationY; >- >- if (lineCount > 0) { >- sourceY = Math.max(0, y + lineHeight); >- destinationY = sourceY + lineCount * lineHeight; >- } >- else { >- destinationY = Math.max(0, y + lineHeight); >- sourceY = destinationY - lineCount * lineHeight; >- } >- scroll( >- 0, destinationY, // destination x, y >- 0, sourceY, // source x, y >- clientArea.width, clientArea.height, true); >- // Always redrawing causes the bottom line to flash when a line is >- // deleted. This is because SWT merges the paint area of the scroll >- // with the paint area of the redraw call below. >- // To prevent this we could call update after the scroll. However, >- // adding update can cause even more flash if the client does other >- // redraw/update calls (ie. for syntax highlighting). >- // We could also redraw only when a line has been added or when >- // contents has been added to a line. This would require getting >- // line index info from the content and is not worth the trouble >- // (the flash is only on the bottom line and minor). >- // Specifying the NO_MERGE_PAINTS style bit prevents the merged >- // redraw but could cause flash/slowness elsewhere. >- if (y + lineHeight > 0 && y <= clientArea.height) { >- // redraw first changed line in case a line was split/joined >- super.redraw(0, y, clientArea.width, lineHeight, true); >- } >- if (newLineCount > 0) { >- int redrawStartY = y + lineHeight; >- int redrawHeight = newLineCount * lineHeight; >- >- if (redrawStartY + redrawHeight > 0 && redrawStartY <= clientArea.height) { >- // display new text >- super.redraw(0, redrawStartY, clientArea.width, redrawHeight, true); >- } >- } >+ Rectangle clientArea = getClientArea(); >+ int lineCount = newLineCount - replacedLineCount; >+ int sourceY; >+ int destinationY; >+ >+ if (lineCount > 0) { >+ sourceY = Math.max(0, y + lineHeight); >+ destinationY = sourceY + lineCount * lineHeight; >+ } >+ else { >+ destinationY = Math.max(0, y + lineHeight); >+ sourceY = destinationY - lineCount * lineHeight; >+ } >+ scroll( >+ 0, destinationY, // destination x, y >+ 0, sourceY, // source x, y >+ clientArea.width, clientArea.height, true); >+ // Always redrawing causes the bottom line to flash when a line is >+ // deleted. This is because SWT merges the paint area of the scroll >+ // with the paint area of the redraw call below. >+ // To prevent this we could call update after the scroll. However, >+ // adding update can cause even more flash if the client does other >+ // redraw/update calls (ie. for syntax highlighting). >+ // We could also redraw only when a line has been added or when >+ // contents has been added to a line. This would require getting >+ // line index info from the content and is not worth the trouble >+ // (the flash is only on the bottom line and minor). >+ // Specifying the NO_MERGE_PAINTS style bit prevents the merged >+ // redraw but could cause flash/slowness elsewhere. >+ if (y + lineHeight > 0 && y <= clientArea.height) { >+ // redraw first changed line in case a line was split/joined >+ super.redraw(0, y, clientArea.width, lineHeight, true); >+ } >+ if (newLineCount > 0) { >+ int redrawStartY = y + lineHeight; >+ int redrawHeight = newLineCount * lineHeight; >+ >+ if (redrawStartY + redrawHeight > 0 && redrawStartY <= clientArea.height) { >+ // display new text >+ super.redraw(0, redrawStartY, clientArea.width, redrawHeight, true); >+ } >+ } > } >-/** >+/** > * Redraws the specified text range. > * <p> > * >@@ -6167,38 +6235,38 @@ > * @param clearBackground true if the background should be cleared as > * part of the redraw operation. If true, the entire redraw range will > * be cleared before anything is redrawn. If the redraw range includes >- * the last character of a line (i.e., the entire line is redrawn) the >- * line is cleared all the way to the right border of the widget. >- * The redraw operation will be faster and smoother if clearBackground >- * is set to false. Whether or not the flag can be set to false depends >- * on the type of change that has taken place. If font styles or >- * background colors for the redraw range have changed, clearBackground >- * should be set to true. If only foreground colors have changed for >- * the redraw range, clearBackground can be set to false. >+ * the last character of a line (i.e., the entire line is redrawn) the >+ * line is cleared all the way to the right border of the widget. >+ * The redraw operation will be faster and smoother if clearBackground >+ * is set to false. Whether or not the flag can be set to false depends >+ * on the type of change that has taken place. If font styles or >+ * background colors for the redraw range have changed, clearBackground >+ * should be set to true. If only foreground colors have changed for >+ * the redraw range, clearBackground can be set to false. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> >+ * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> > * </ul> > */ > public void redrawRange(int start, int length, boolean clearBackground) { >- checkWidget(); >- int end = start + length; >- int contentLength = content.getCharCount(); >- int firstLine; >- int lastLine; >- >- if (start > end || start < 0 || end > contentLength) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- firstLine = content.getLineAtOffset(start); >- lastLine = content.getLineAtOffset(end); >- // reset all affected lines but let the redraw recalculate only >- // those that are visible. >- lineCache.reset(firstLine, lastLine - firstLine + 1, true); >- internalRedrawRange(start, length, clearBackground); >+ checkWidget(); >+ int end = start + length; >+ int contentLength = content.getCharCount(); >+ int firstLine; >+ int lastLine; >+ >+ if (start > end || start < 0 || end > contentLength) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ firstLine = content.getLineAtOffset(start); >+ lastLine = content.getLineAtOffset(end); >+ // reset all affected lines but let the redraw recalculate only >+ // those that are visible. >+ lineCache.reset(firstLine, lastLine - firstLine + 1, true); >+ internalRedrawRange(start, length, clearBackground); > } > /** > * Removes the specified bidirectional segment listener. >@@ -6215,9 +6283,9 @@ > * @since 2.0 > */ > public void removeBidiSegmentListener(BidiSegmentListener listener) { >- checkWidget(); >- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >- removeListener(LineGetSegments, listener); >+ checkWidget(); >+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ removeListener(LineGetSegments, listener); > } > /** > * Removes the specified extended modify listener. >@@ -6233,9 +6301,9 @@ > * </ul> > */ > public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) { >- checkWidget(); >- if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >- removeListener(ExtendedModify, extendedModifyListener); >+ checkWidget(); >+ if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ removeListener(ExtendedModify, extendedModifyListener); > } > /** > * Removes the specified line background listener. >@@ -6251,15 +6319,15 @@ > * </ul> > */ > public void removeLineBackgroundListener(LineBackgroundListener listener) { >- checkWidget(); >- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >- removeListener(LineGetBackground, listener); >- // use default line styler if last user line styler was removed. >- if (isListening(LineGetBackground) == false && userLineBackground) { >- StyledTextListener typedListener = new StyledTextListener(defaultLineStyler); >- addListener(LineGetBackground, typedListener); >- userLineBackground = false; >- } >+ checkWidget(); >+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ removeListener(LineGetBackground, listener); >+ // use default line styler if last user line styler was removed. >+ if (isListening(LineGetBackground) == false && userLineBackground) { >+ StyledTextListener typedListener = new StyledTextListener(defaultLineStyler); >+ addListener(LineGetBackground, typedListener); >+ userLineBackground = false; >+ } > } > /** > * Removes the specified line style listener. >@@ -6275,17 +6343,17 @@ > * </ul> > */ > public void removeLineStyleListener(LineStyleListener listener) { >- checkWidget(); >- if (listener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- removeListener(LineGetStyle, listener); >- // use default line styler if last user line styler was removed. Fixes 1G7B1X2 >- if (isListening(LineGetStyle) == false && userLineStyle) { >- StyledTextListener typedListener = new StyledTextListener(defaultLineStyler); >- addListener(LineGetStyle, typedListener); >- userLineStyle = false; >- } >+ checkWidget(); >+ if (listener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ removeListener(LineGetStyle, listener); >+ // use default line styler if last user line styler was removed. Fixes 1G7B1X2 >+ if (isListening(LineGetStyle) == false && userLineStyle) { >+ StyledTextListener typedListener = new StyledTextListener(defaultLineStyler); >+ addListener(LineGetStyle, typedListener); >+ userLineStyle = false; >+ } > } > /** > * Removes the specified modify listener. >@@ -6301,11 +6369,11 @@ > * </ul> > */ > public void removeModifyListener(ModifyListener modifyListener) { >- checkWidget(); >- if (modifyListener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- removeListener(SWT.Modify, modifyListener); >+ checkWidget(); >+ if (modifyListener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ removeListener(SWT.Modify, modifyListener); > } > /** > * Removes the specified selection listener. >@@ -6321,11 +6389,11 @@ > * </ul> > */ > public void removeSelectionListener(SelectionListener listener) { >- checkWidget(); >- if (listener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- removeListener(SWT.Selection, listener); >+ checkWidget(); >+ if (listener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ removeListener(SWT.Selection, listener); > } > /** > * Removes the specified verify listener. >@@ -6341,11 +6409,11 @@ > * </ul> > */ > public void removeVerifyListener(VerifyListener verifyListener) { >- checkWidget(); >- if (verifyListener == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- removeListener(SWT.Verify, verifyListener); >+ checkWidget(); >+ if (verifyListener == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ removeListener(SWT.Verify, verifyListener); > } > /** > * Removes the specified key verify listener. >@@ -6361,22 +6429,22 @@ > * </ul> > */ > public void removeVerifyKeyListener(VerifyKeyListener listener) { >- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >- removeListener(VerifyKey, listener); >+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ removeListener(VerifyKey, listener); > } >-/** >+/** > * Replaces the styles in the given range with new styles. This method > * effectively deletes the styles in the given range and then adds the >- * the new styles. >+ * the new styles. > * <p> >- * Should not be called if a LineStyleListener has been set since the >+ * Should not be called if a LineStyleListener has been set since the > * listener maintains the styles. > * </p> > * > * @param start offset of first character where styles will be deleted > * @param length length of the range to delete styles in > * @param ranges StyleRange objects containing the new style information. >- * The ranges should not overlap and should be within the specified start >+ * The ranges should not overlap and should be within the specified start > * and length. The style rendering is undefined if the ranges do overlap > * or are ill-defined. Must not be null. > * @exception SWTException <ul> >@@ -6384,93 +6452,93 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> >+ * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> > * <li>ERROR_NULL_ARGUMENT when string is null</li> > * </ul> > * @since 2.0 > */ > public void replaceStyleRanges(int start, int length, StyleRange[] ranges) { >- checkWidget(); >- if (userLineStyle) { >- return; >- } >- if (ranges == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- if (ranges.length == 0) { >- setStyleRange(new StyleRange(start, length, null, null)); >- return; >- } >- int end = start + length; >- if (start > end || start < 0 || end > getCharCount()) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- >- int firstLine = content.getLineAtOffset(start); >- int lastLine = content.getLineAtOffset(end); >- >- // if the area is not visible, there is no need to redraw >- boolean redrawLines = isAreaVisible(firstLine, lastLine); >- >- if (!redrawLines) { >- defaultLineStyler.replaceStyleRanges(start, length, ranges); >- lineCache.reset(firstLine, lastLine - firstLine + 1, true); >- } else { >- boolean redrawFirstLine = false; >- boolean redrawLastLine = false; >- // the first and last line needs to be redrawn completely if the >- // font style is changing from SWT.NORMAL to something else or >- // vice versa. fixes 1G7M5WE. >- int firstLineOffset = content.getOffsetAtLine(firstLine); >- if (isBidi()) { >- redrawFirstLine = true; >- redrawLastLine = true; >- } else { >- int firstLineEnd = firstLineOffset + content.getLine(firstLine).length(); >- redrawFirstLine = isRedrawFirstLine(ranges, firstLine, firstLineOffset); >- // check if any bold styles will be cleared >- StyleRange clearRange = new StyleRange(firstLineOffset, firstLineEnd - firstLineOffset, null, null); >- redrawFirstLine = redrawFirstLine || isRedrawFirstLine(new StyleRange[] {clearRange}, firstLine, firstLineOffset); >- if (lastLine != firstLine) { >- int lastLineOffset = content.getOffsetAtLine(lastLine); >- int lastLineEnd = lastLineOffset + content.getLine(lastLine).length(); >- redrawLastLine = isRedrawLastLine(ranges, lastLine, lastLineOffset); >- // check if any bold styles will be cleared >- clearRange = new StyleRange(lastLineOffset, lastLineEnd - lastLineOffset, null, null); >- redrawLastLine = redrawLastLine || isRedrawLastLine(new StyleRange[] {clearRange}, lastLine, lastLineOffset); >- } >- } >- defaultLineStyler.replaceStyleRanges(start, length, ranges); >- // reset all lines affected by the style change but let the redraw >- // recalculate only those that are visible. >- lineCache.reset(firstLine, lastLine - firstLine + 1, true); >- internalRedrawRange(start, length, true); >- if (redrawFirstLine) { >- redrawLine(firstLine, start - firstLineOffset); >- } >- if (redrawLastLine) { >- redrawLine(lastLine, 0); >- } >- } >- >- // make sure that the caret is positioned correctly. >- // caret location may change if font style changes. >- // fixes 1G8FODP >- setCaretLocation(); >+ checkWidget(); >+ if (userLineStyle) { >+ return; >+ } >+ if (ranges == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ if (ranges.length == 0) { >+ setStyleRange(new StyleRange(start, length, null, null)); >+ return; >+ } >+ int end = start + length; >+ if (start > end || start < 0 || end > getCharCount()) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ >+ int firstLine = content.getLineAtOffset(start); >+ int lastLine = content.getLineAtOffset(end); >+ >+ // if the area is not visible, there is no need to redraw >+ boolean redrawLines = isAreaVisible(firstLine, lastLine); >+ >+ if (!redrawLines) { >+ defaultLineStyler.replaceStyleRanges(start, length, ranges); >+ lineCache.reset(firstLine, lastLine - firstLine + 1, true); >+ } else { >+ boolean redrawFirstLine = false; >+ boolean redrawLastLine = false; >+ // the first and last line needs to be redrawn completely if the >+ // font style is changing from SWT.NORMAL to something else or >+ // vice versa. fixes 1G7M5WE. >+ int firstLineOffset = content.getOffsetAtLine(firstLine); >+ if (isBidi()) { >+ redrawFirstLine = true; >+ redrawLastLine = true; >+ } else { >+ int firstLineEnd = firstLineOffset + content.getLine(firstLine).length(); >+ redrawFirstLine = isRedrawFirstLine(ranges, firstLine, firstLineOffset); >+ // check if any bold styles will be cleared >+ StyleRange clearRange = new StyleRange(firstLineOffset, firstLineEnd - firstLineOffset, null, null); >+ redrawFirstLine = redrawFirstLine || isRedrawFirstLine(new StyleRange[] {clearRange}, firstLine, firstLineOffset); >+ if (lastLine != firstLine) { >+ int lastLineOffset = content.getOffsetAtLine(lastLine); >+ int lastLineEnd = lastLineOffset + content.getLine(lastLine).length(); >+ redrawLastLine = isRedrawLastLine(ranges, lastLine, lastLineOffset); >+ // check if any bold styles will be cleared >+ clearRange = new StyleRange(lastLineOffset, lastLineEnd - lastLineOffset, null, null); >+ redrawLastLine = redrawLastLine || isRedrawLastLine(new StyleRange[] {clearRange}, lastLine, lastLineOffset); >+ } >+ } >+ defaultLineStyler.replaceStyleRanges(start, length, ranges); >+ // reset all lines affected by the style change but let the redraw >+ // recalculate only those that are visible. >+ lineCache.reset(firstLine, lastLine - firstLine + 1, true); >+ internalRedrawRange(start, length, true); >+ if (redrawFirstLine) { >+ redrawLine(firstLine, start - firstLineOffset); >+ } >+ if (redrawLastLine) { >+ redrawLine(lastLine, 0); >+ } >+ } >+ >+ // make sure that the caret is positioned correctly. >+ // caret location may change if font style changes. >+ // fixes 1G8FODP >+ setCaretLocation(); > } > /** > * Replaces the given text range with new text. >- * If the widget has the SWT.SINGLE style and "text" contains more than >- * one line, only the first line is rendered but the text is stored >- * unchanged. A subsequent call to getText will return the same text >- * that was set. Note that only a single line of text should be set when >+ * If the widget has the SWT.SINGLE style and "text" contains more than >+ * one line, only the first line is rendered but the text is stored >+ * unchanged. A subsequent call to getText will return the same text >+ * that was set. Note that only a single line of text should be set when > * the SWT.SINGLE style is used. > * <p> > * <b>NOTE:</b> During the replace operation the current selection is > * changed as follows: >- * <ul> >+ * <ul> > * <li>selection before replaced text: selection unchanged >- * <li>selection after replaced text: adjust the selection so that same text >+ * <li>selection after replaced text: adjust the selection so that same text > * remains selected > * <li>selection intersects replaced text: selection is cleared and caret > * is placed after inserted text >@@ -6485,115 +6553,117 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> >- * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter. >- * Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li> >+ * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> >+ * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter. >+ * Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li> > * <li>ERROR_NULL_ARGUMENT when string is null</li> > * </ul> > */ > public void replaceTextRange(int start, int length, String text) { >- checkWidget(); >- int contentLength = getCharCount(); >- int end = start + length; >- Event event = new Event(); >- >- if (start > end || start < 0 || end > contentLength) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- if (text == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- event.start = start; >- event.end = end; >- event.text = text; >- modifyContent(event, false); >+ checkWidget(); >+ int contentLength = getCharCount(); >+ int end = start + length; >+ Event event = new Event(); >+ >+ if (start > end || start < 0 || end > contentLength) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ if (text == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ event.start = start; >+ event.end = end; >+ event.text = text; >+ modifyContent(event, false); > } > /** > * Resets the caret position, selection and scroll offsets. Recalculate > * the content width and scroll bars. Redraw the widget. > */ > void reset() { >- ScrollBar verticalBar = getVerticalBar(); >- ScrollBar horizontalBar = getHorizontalBar(); >- caretOffset = 0; >- topIndex = 0; >- topOffset = 0; >- verticalScrollOffset = 0; >- horizontalScrollOffset = 0; >- resetSelection(); >- // discard any styles that may have been set by creating a >- // new default line styler >- if (defaultLineStyler != null) { >- removeLineBackgroundListener(defaultLineStyler); >- removeLineStyleListener(defaultLineStyler); >- installDefaultLineStyler(); >- } >- calculateContentWidth(); >- if (verticalBar != null) { >- verticalBar.setSelection(0); >- } >- if (horizontalBar != null) { >- horizontalBar.setSelection(0); >- } >- setScrollBars(); >- setCaretLocation(); >- super.redraw(); >+ ScrollBar verticalBar = getVerticalBar(); >+ ScrollBar horizontalBar = getHorizontalBar(); >+ caretOffset = 0; >+ topIndex = 0; >+ topOffset = 0; >+ verticalScrollOffset = 0; >+ horizontalScrollOffset = 0; >+ resetSelection(); >+ // discard any styles that may have been set by creating a >+ // new default line styler >+ if (defaultLineStyler != null) { >+ removeLineBackgroundListener(defaultLineStyler); >+ removeLineStyleListener(defaultLineStyler); >+ installDefaultLineStyler(); >+ } >+ calculateContentWidth(); >+ if (verticalBar != null) { >+ verticalBar.setSelection(0); >+ } >+ if (horizontalBar != null) { >+ horizontalBar.setSelection(0); >+ } >+ setScrollBars(); >+ setCaretLocation(); >+ super.redraw(); > } > /** > * Resets the selection. > */ > void resetSelection() { >- selection.x = selection.y = caretOffset; >- selectionAnchor = -1; >+ selection.x = selection.y = caretOffset; >+ selectionAnchor = -1; >+ // see if we need to update the mouse pointer >+ internalSetCursor(); > } > /** > * Scrolls the widget horizontally. > * <p> > * > * @param pixels number of pixels to scroll, > 0 = scroll left, >- * < 0 scroll right >+ * < 0 scroll right > */ > void scrollHorizontal(int pixels) { >- Rectangle clientArea; >- >- if (pixels == 0) { >- return; >- } >- clientArea = getClientArea(); >- if (pixels > 0) { >- int sourceX = leftMargin + pixels; >- int scrollWidth = clientArea.width - sourceX - rightMargin; >- int scrollHeight = clientArea.height - topMargin - bottomMargin; >- scroll( >- leftMargin, topMargin, // destination x, y >- sourceX, topMargin, // source x, y >- scrollWidth, scrollHeight, true); >- if (sourceX > scrollWidth) { >- // redraw from end of scrolled area to beginning of scroll >- // invalidated area >- super.redraw( >- leftMargin + scrollWidth, topMargin, >- pixels - scrollWidth, scrollHeight, true); >- } >- } >- else { >- int destinationX = leftMargin - pixels; >- int scrollWidth = clientArea.width - destinationX - rightMargin; >- int scrollHeight = clientArea.height - topMargin - bottomMargin; >- scroll( >- destinationX, topMargin, // destination x, y >- leftMargin, topMargin, // source x, y >- scrollWidth, scrollHeight, true); >- if (destinationX > scrollWidth) { >- // redraw from end of scroll invalidated area to scroll >- // destination >- super.redraw( >- leftMargin + scrollWidth, topMargin, >- -pixels - scrollWidth, scrollHeight, true); >- } >- } >- horizontalScrollOffset += pixels; >- setCaretLocation(); >+ Rectangle clientArea; >+ >+ if (pixels == 0) { >+ return; >+ } >+ clientArea = getClientArea(); >+ if (pixels > 0) { >+ int sourceX = leftMargin + pixels; >+ int scrollWidth = clientArea.width - sourceX - rightMargin; >+ int scrollHeight = clientArea.height - topMargin - bottomMargin; >+ scroll( >+ leftMargin, topMargin, // destination x, y >+ sourceX, topMargin, // source x, y >+ scrollWidth, scrollHeight, true); >+ if (sourceX > scrollWidth) { >+ // redraw from end of scrolled area to beginning of scroll >+ // invalidated area >+ super.redraw( >+ leftMargin + scrollWidth, topMargin, >+ pixels - scrollWidth, scrollHeight, true); >+ } >+ } >+ else { >+ int destinationX = leftMargin - pixels; >+ int scrollWidth = clientArea.width - destinationX - rightMargin; >+ int scrollHeight = clientArea.height - topMargin - bottomMargin; >+ scroll( >+ destinationX, topMargin, // destination x, y >+ leftMargin, topMargin, // source x, y >+ scrollWidth, scrollHeight, true); >+ if (destinationX > scrollWidth) { >+ // redraw from end of scroll invalidated area to scroll >+ // destination >+ super.redraw( >+ leftMargin + scrollWidth, topMargin, >+ -pixels - scrollWidth, scrollHeight, true); >+ } >+ } >+ horizontalScrollOffset += pixels; >+ setCaretLocation(); > } > /** > * Scrolls the widget horizontally and adjust the horizontal scroll >@@ -6601,23 +6671,23 @@ > * <p> > * > * @param pixels number of pixels to scroll, > 0 = scroll left, >- * < 0 scroll right >+ * < 0 scroll right > * @return >- * true=the widget was scrolled >- * false=the widget was not scrolled, the given offset is not valid. >+ * true=the widget was scrolled >+ * false=the widget was not scrolled, the given offset is not valid. > */ > boolean scrollHorizontalBar(int pixels) { >- if (pixels == 0) { >- return false; >- } >- ScrollBar horizontalBar = getHorizontalBar(); >- if (horizontalBar != null) { >- horizontalBar.setSelection(horizontalScrollOffset + pixels); >- } >- scrollHorizontal(pixels); >- return true; >+ if (pixels == 0) { >+ return false; >+ } >+ ScrollBar horizontalBar = getHorizontalBar(); >+ if (horizontalBar != null) { >+ horizontalBar.setSelection(horizontalScrollOffset + pixels); >+ } >+ scrollHorizontal(pixels); >+ return true; > } >-/** >+/** > * Selects all the text. > * <p> > * >@@ -6627,34 +6697,34 @@ > * </ul> > */ > public void selectAll() { >- checkWidget(); >- setSelection(new Point(0, Math.max(getCharCount(),0))); >+ checkWidget(); >+ setSelection(new Point(0, Math.max(getCharCount(),0))); > } > /** > * Replaces/inserts text as defined by the event. > * <p> > * >- * @param event the text change event. >- * <ul> >- * <li>event.start - the replace start offset</li> >- * <li>event.end - the replace end offset</li> >- * <li>event.text - the new text</li> >- * </ul> >+ * @param event the text change event. >+ * <ul> >+ * <li>event.start - the replace start offset</li> >+ * <li>event.end - the replace end offset</li> >+ * <li>event.text - the new text</li> >+ * </ul> > */ > void sendKeyEvent(Event event) { >- if (editable == false) { >- return; >- } >- modifyContent(event, true); >+ if (editable == false) { >+ return; >+ } >+ modifyContent(event, true); > } > /** > * Sends the specified selection event. > */ > void sendSelectionEvent() { >- Event event = new Event(); >- event.x = selection.x; >- event.y = selection.y; >- notifyListeners(SWT.Selection, event); >+ Event event = new Event(); >+ event.x = selection.x; >+ event.y = selection.y; >+ notifyListeners(SWT.Selection, event); > } > /** > * Sets whether the widget wraps lines. >@@ -6665,59 +6735,59 @@ > * @since 2.0 > */ > public void setWordWrap(boolean wrap) { >- checkWidget(); >- >- if (wrap != wordWrap) { >- ScrollBar horizontalBar = getHorizontalBar(); >- >- wordWrap = wrap; >- if (wordWrap) { >- logicalContent = content; >- content = new WrappedContent(renderer, logicalContent); >- } >- else { >- content = logicalContent; >- } >- calculateContentWidth(); >- horizontalScrollOffset = 0; >- if (horizontalBar != null) { >- horizontalBar.setVisible(!wordWrap); >- } >- setScrollBars(); >- setCaretLocation(); >- super.redraw(); >- } >+ checkWidget(); >+ >+ if (wrap != wordWrap) { >+ ScrollBar horizontalBar = getHorizontalBar(); >+ >+ wordWrap = wrap; >+ if (wordWrap) { >+ logicalContent = content; >+ content = new WrappedContent(renderer, logicalContent); >+ } >+ else { >+ content = logicalContent; >+ } >+ calculateContentWidth(); >+ horizontalScrollOffset = 0; >+ if (horizontalBar != null) { >+ horizontalBar.setVisible(!wordWrap); >+ } >+ setScrollBars(); >+ setCaretLocation(); >+ super.redraw(); >+ } > } > /** > * Sets the caret location and scrolls the caret offset into view. > */ > void showBidiCaret() { >- int line = content.getLineAtOffset(caretOffset); >- int lineOffset = content.getOffsetAtLine(line); >- int offsetInLine = caretOffset - lineOffset; >- String lineText = content.getLine(line); >- int xAtOffset = 0; >- boolean scrolled = false; >- GC gc = getGC(); >- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >- // getXAtOffset, inlined for better performance >- xAtOffset = getBidiTextPosition(lineText, offsetInLine, bidi) + leftMargin; >- if (offsetInLine > lineText.length()) { >- // offset is not on the line. return an x location one character >- // after the line to indicate the line delimiter. >- xAtOffset += lineEndSpaceWidth; >- } >- xAtOffset -= horizontalScrollOffset; >- // >- scrolled = showLocation(xAtOffset, line); >- if (scrolled == false) { >- setBidiCaretLocation(bidi); >- } >- gc.dispose(); >+ int line = content.getLineAtOffset(caretOffset); >+ int lineOffset = content.getOffsetAtLine(line); >+ int offsetInLine = caretOffset - lineOffset; >+ String lineText = content.getLine(line); >+ int xAtOffset = 0; >+ boolean scrolled = false; >+ GC gc = getGC(); >+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc); >+ // getXAtOffset, inlined for better performance >+ xAtOffset = getBidiTextPosition(lineText, offsetInLine, bidi) + leftMargin; >+ if (offsetInLine > lineText.length()) { >+ // offset is not on the line. return an x location one character >+ // after the line to indicate the line delimiter. >+ xAtOffset += lineEndSpaceWidth; >+ } >+ xAtOffset -= horizontalScrollOffset; >+ // >+ scrolled = showLocation(xAtOffset, line); >+ if (scrolled == false) { >+ setBidiCaretLocation(bidi); >+ } >+ gc.dispose(); > } > /** > * Sets the receiver's caret. Set the caret's height and location. >- * >+ * > * </p> > * @param caret the new caret for the receiver > * >@@ -6727,100 +6797,100 @@ > * </ul> > */ > public void setCaret(Caret caret) { >- checkWidget (); >- super.setCaret(caret); >- if (caret != null) { >- if (isBidi() == false) { >- caret.setSize(caret.getSize().x, lineHeight); >- } >- setCaretLocation(); >- if (isBidi()) { >- setBidiKeyboardLanguage(); >- } >- } >+ checkWidget (); >+ super.setCaret(caret); >+ if (caret != null) { >+ if (isBidi() == false) { >+ caret.setSize(caret.getSize().x, lineHeight); >+ } >+ setCaretLocation(); >+ if (isBidi()) { >+ setBidiKeyboardLanguage(); >+ } >+ } > } > /** > * @see org.eclipse.swt.widgets.Control#setBackground > */ > public void setBackground(Color color) { >- checkWidget(); >- super.setBackground(color); >- background = color; >- redraw(); >+ checkWidget(); >+ super.setBackground(color); >+ background = color; >+ redraw(); > } > /** > * Set the caret to indicate the current typing direction. > */ > void setBidiCaretDirection() { >- Caret caret = getCaret(); >- int direction = StyledTextBidi.getKeyboardLanguageDirection(); >- >- if (caret == null || direction == caretDirection) { >- return; >- } >- caretDirection = direction; >- if (direction == SWT.DEFAULT) { >- caret.setImage(null); >- caret.setSize(caret.getSize().x, lineHeight); >- } >- else >- if (caretDirection == SWT.LEFT) { >- caret.setImage(leftCaretBitmap); >- } >- else >- if (caretDirection == SWT.RIGHT) { >- caret.setImage(rightCaretBitmap); >- } >+ Caret caret = getCaret(); >+ int direction = StyledTextBidi.getKeyboardLanguageDirection(); >+ >+ if (caret == null || direction == caretDirection) { >+ return; >+ } >+ caretDirection = direction; >+ if (direction == SWT.DEFAULT) { >+ caret.setImage(null); >+ caret.setSize(caret.getSize().x, lineHeight); >+ } >+ else >+ if (caretDirection == SWT.LEFT) { >+ caret.setImage(leftCaretBitmap); >+ } >+ else >+ if (caretDirection == SWT.RIGHT) { >+ caret.setImage(rightCaretBitmap); >+ } > } > /** > * Moves the Caret to the current caret offset. > * <p> >- * >+ * > * @param bidi StyledTextBidi object to use for measuring. >- * May be left null in which case a new object will be created. >+ * May be left null in which case a new object will be created. > */ > void setBidiCaretLocation(StyledTextBidi bidi) { >- int caretLine = getCaretLine(); >- >- setBidiCaretLocation(bidi, caretLine); >+ int caretLine = getCaretLine(); >+ >+ setBidiCaretLocation(bidi, caretLine); > } > /** > * Moves the Caret to the current caret offset. > * <p> >- * >+ * > * @param bidi StyledTextBidi object to use for measuring. >- * May be left null in which case a new object will be created. >+ * May be left null in which case a new object will be created. > * @param caretLine line the caret should be placed on. Relative to >- * first line in document >+ * first line in document > */ > void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) { >- Caret caret = getCaret(); >- String lineText = content.getLine(caretLine); >- int lineStartOffset = content.getOffsetAtLine(caretLine); >- int offsetInLine = caretOffset - lineStartOffset; >- GC gc = null; >- >- if (bidi == null) { >- gc = getGC(); >- bidi = getStyledTextBidi(lineText, lineStartOffset, gc); >- } >- if (lastCaretDirection == SWT.NULL) { >- columnX = bidi.getTextPosition(offsetInLine) + leftMargin - horizontalScrollOffset; >- } else { >- columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset; >- } >- if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) { >- columnX -= (getCaretWidth() - 1); >- } >- if (caret != null) { >- setBidiCaretDirection(); >- caret.setLocation( >- columnX, >- caretLine * lineHeight - verticalScrollOffset + topMargin); >- } >- if (gc != null) { >- gc.dispose(); >- } >+ Caret caret = getCaret(); >+ String lineText = content.getLine(caretLine); >+ int lineStartOffset = content.getOffsetAtLine(caretLine); >+ int offsetInLine = caretOffset - lineStartOffset; >+ GC gc = null; >+ >+ if (bidi == null) { >+ gc = getGC(); >+ bidi = getStyledTextBidi(lineText, lineStartOffset, gc); >+ } >+ if (lastCaretDirection == SWT.NULL) { >+ columnX = bidi.getTextPosition(offsetInLine) + leftMargin - horizontalScrollOffset; >+ } else { >+ columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset; >+ } >+ if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) { >+ columnX -= (getCaretWidth() - 1); >+ } >+ if (caret != null) { >+ setBidiCaretDirection(); >+ caret.setLocation( >+ columnX, >+ caretLine * lineHeight - verticalScrollOffset + topMargin); >+ } >+ if (gc != null) { >+ gc.dispose(); >+ } > } > /** > * Sets the BIDI coloring mode. When true the BIDI text display >@@ -6837,87 +6907,87 @@ > * </p> > */ > public void setBidiColoring(boolean mode) { >- checkWidget(); >- bidiColoring = mode; >+ checkWidget(); >+ bidiColoring = mode; > } > /** >- * Switches the keyboard language according to the current editing >+ * Switches the keyboard language according to the current editing > * position and cursor direction. > */ > void setBidiKeyboardLanguage() { >- int caretLine = getCaretLine(); >- int lineStartOffset = content.getOffsetAtLine(caretLine); >- int offsetInLine = caretOffset - lineStartOffset; >- String lineText = content.getLine(caretLine); >- GC gc = getGC(); >- StyledTextBidi bidi; >- int lineLength = lineText.length(); >- >- // Don't supply the bold styles/font since we don't want to measure anything >- bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText)); >- if (offsetInLine == 0) { >- bidi.setKeyboardLanguage(offsetInLine); >- } >- else >- if (offsetInLine >= lineLength) { >- offsetInLine = Math.min(offsetInLine, lineLength - 1); >- bidi.setKeyboardLanguage(offsetInLine); >- } >- else >- if (lastCaretDirection == ST.COLUMN_NEXT) { >- // continue with previous character type >- bidi.setKeyboardLanguage(offsetInLine - 1); >- } >- else { >- bidi.setKeyboardLanguage(offsetInLine); >- } >- gc.dispose(); >+ int caretLine = getCaretLine(); >+ int lineStartOffset = content.getOffsetAtLine(caretLine); >+ int offsetInLine = caretOffset - lineStartOffset; >+ String lineText = content.getLine(caretLine); >+ GC gc = getGC(); >+ StyledTextBidi bidi; >+ int lineLength = lineText.length(); >+ >+ // Don't supply the bold styles/font since we don't want to measure anything >+ bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText)); >+ if (offsetInLine == 0) { >+ bidi.setKeyboardLanguage(offsetInLine); >+ } >+ else >+ if (offsetInLine >= lineLength) { >+ offsetInLine = Math.min(offsetInLine, lineLength - 1); >+ bidi.setKeyboardLanguage(offsetInLine); >+ } >+ else >+ if (lastCaretDirection == ST.COLUMN_NEXT) { >+ // continue with previous character type >+ bidi.setKeyboardLanguage(offsetInLine - 1); >+ } >+ else { >+ bidi.setKeyboardLanguage(offsetInLine); >+ } >+ gc.dispose(); > } > /** > * Moves the Caret to the current caret offset. > * <p> >- * >+ * > * @param newCaretX the new x location of the caret. >- * passed in for better performance when it has already been >- * calculated outside this method. >- * @param line index of the line the caret is on. Relative to >- * the first line in the document. >+ * passed in for better performance when it has already been >+ * calculated outside this method. >+ * @param line index of the line the caret is on. Relative to >+ * the first line in the document. > */ > void setCaretLocation(int newCaretX, int line) { >- if (isBidi()) { >- setBidiCaretLocation(null, line); >- } >- else { >- Caret caret = getCaret(); >- >- columnX = newCaretX; >- if (caret != null) { >- caret.setLocation( >- newCaretX, >- line * lineHeight - verticalScrollOffset + topMargin); >- } >- } >+ if (isBidi()) { >+ setBidiCaretLocation(null, line); >+ } >+ else { >+ Caret caret = getCaret(); >+ >+ columnX = newCaretX; >+ if (caret != null) { >+ caret.setLocation( >+ newCaretX, >+ line * lineHeight - verticalScrollOffset + topMargin); >+ } >+ } > } > /** > * Moves the Caret to the current caret offset. > */ > void setCaretLocation() { >- if (isBidi()) { >- setBidiCaretLocation(null); >- } >- else { >- Caret caret = getCaret(); >- int caretLine = getCaretLine(); >- int lineStartOffset = content.getOffsetAtLine(caretLine); >- >- columnX = getXAtOffset( >- content.getLine(caretLine), caretLine, caretOffset - lineStartOffset); >- if (caret != null) { >- caret.setLocation( >- columnX, >- caretLine * lineHeight - verticalScrollOffset + topMargin); >- } >- } >+ if (isBidi()) { >+ setBidiCaretLocation(null); >+ } >+ else { >+ Caret caret = getCaret(); >+ int caretLine = getCaretLine(); >+ int lineStartOffset = content.getOffsetAtLine(caretLine); >+ >+ columnX = getXAtOffset( >+ content.getLine(caretLine), caretLine, caretOffset - lineStartOffset); >+ if (caret != null) { >+ caret.setLocation( >+ columnX, >+ caretLine * lineHeight - verticalScrollOffset + topMargin); >+ } >+ } > } > /** > * Sets the caret offset. >@@ -6928,40 +6998,40 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a >+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a > * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter) > * </ul> > */ > public void setCaretOffset(int offset) { >- checkWidget(); >- int length = getCharCount(); >- >- if (length > 0 && offset != caretOffset) { >- if (offset < 0) { >- caretOffset = 0; >- } >- else >- if (offset > length) { >- caretOffset = length; >- } >- else { >- if (isLineDelimiter(offset)) { >- // offset is inside a multi byte line delimiter. This is an >- // illegal operation and an exception is thrown. Fixes 1GDKK3R >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- caretOffset = offset; >- } >- // clear the selection if the caret is moved. >- // don't notify listeners about the selection change. >- clearSelection(false); >- } >- // always update the caret location. fixes 1G8FODP >- setCaretLocation(); >- if (isBidi()) { >- setBidiKeyboardLanguage(); >- } >-} >+ checkWidget(); >+ int length = getCharCount(); >+ >+ if (length > 0 && offset != caretOffset) { >+ if (offset < 0) { >+ caretOffset = 0; >+ } >+ else >+ if (offset > length) { >+ caretOffset = length; >+ } >+ else { >+ if (isLineDelimiter(offset)) { >+ // offset is inside a multi byte line delimiter. This is an >+ // illegal operation and an exception is thrown. Fixes 1GDKK3R >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ caretOffset = offset; >+ } >+ // clear the selection if the caret is moved. >+ // don't notify listeners about the selection change. >+ clearSelection(false); >+ } >+ // always update the caret location. fixes 1G8FODP >+ setCaretLocation(); >+ if (isBidi()) { >+ setBidiKeyboardLanguage(); >+ } >+} > /** > * Copies the specified text range to the clipboard. The text will be placed > * in the clipboard in plain text format and RTF format. >@@ -6969,21 +7039,21 @@ > * > * @param start start index of the text > * @param length length of text to place in clipboard >- * >+ * > * @exception SWTError, see Clipboard.setContents > * @see org.eclipse.swt.dnd.Clipboard.setContents > */ > void setClipboardContent(int start, int length) throws SWTError { >- RTFTransfer rtfTransfer = RTFTransfer.getInstance(); >- TextTransfer plainTextTransfer = TextTransfer.getInstance(); >- RTFWriter rtfWriter = new RTFWriter(start, length); >- TextWriter plainTextWriter = new TextWriter(start, length); >- String rtfText = getPlatformDelimitedText(rtfWriter); >- String plainText = getPlatformDelimitedText(plainTextWriter); >- >- clipboard.setContents( >- new String[]{rtfText, plainText}, >- new Transfer[]{rtfTransfer, plainTextTransfer}); >+ RTFTransfer rtfTransfer = RTFTransfer.getInstance(); >+ TextTransfer plainTextTransfer = TextTransfer.getInstance(); >+ RTFWriter rtfWriter = new RTFWriter(start, length); >+ TextWriter plainTextWriter = new TextWriter(start, length); >+ String rtfText = getPlatformDelimitedText(rtfWriter); >+ String plainText = getPlatformDelimitedText(plainTextWriter); >+ >+ clipboard.setContents( >+ new String[]{rtfText, plainText}, >+ new Transfer[]{rtfTransfer, plainTextTransfer}); > } > /** > * Sets the content implementation to use for text storage. >@@ -6999,66 +7069,67 @@ > * </ul> > */ > public void setContent(StyledTextContent newContent) { >- checkWidget(); >- if (newContent == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- if (content != null) { >- content.removeTextChangeListener(textChangeListener); >- } >- logicalContent = newContent; >- if (wordWrap) { >- content = new WrappedContent(renderer, logicalContent); >- } >- else { >- content = logicalContent; >- } >- content.addTextChangeListener(textChangeListener); >- reset(); >+ checkWidget(); >+ if (newContent == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ if (content != null) { >+ content.removeTextChangeListener(textChangeListener); >+ } >+ logicalContent = newContent; >+ if (wordWrap) { >+ content = new WrappedContent(renderer, logicalContent); >+ } >+ else { >+ content = logicalContent; >+ } >+ content.addTextChangeListener(textChangeListener); >+ reset(); > } > /** > * Sets the receiver's cursor to the cursor specified by the >- * argument. Overridden to handle the null case since the >+ * argument. Overridden to handle the null case since the > * StyledText widget uses an ibeam as its default cursor. > * > * @see org.eclipse.swt.widgets.Control#setCursor > */ > public void setCursor (Cursor cursor) { >- if (cursor == null) { >- super.setCursor(ibeamCursor); >- } else { >- super.setCursor(cursor); >- } >+ if (cursor == null || cursor.equals(ibeamCursor)) { >+ customCursor = null; >+ } else { >+ customCursor = cursor; >+ } >+ internalSetCursor(); > } >-/** >+/** > * Sets whether the widget implements double click mouse behavior. > * </p> > * > * @param enable if true double clicking a word selects the word, if false >- * double clicks have the same effect as regular mouse clicks. >+ * double clicks have the same effect as regular mouse clicks. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > public void setDoubleClickEnabled(boolean enable) { >- checkWidget(); >- doubleClickEnabled = enable; >+ checkWidget(); >+ doubleClickEnabled = enable; > } > /** > * Sets whether the widget content can be edited. > * </p> > * >- * @param editable if true content can be edited, if false content can not be >- * edited >+ * @param editable if true content can be edited, if false content can not be >+ * edited > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > public void setEditable(boolean editable) { >- checkWidget(); >- this.editable = editable; >+ checkWidget(); >+ this.editable = editable; > } > /** > * Sets a new font to render text with. >@@ -7074,93 +7145,93 @@ > * </ul> > */ > public void setFont(Font font) { >- checkWidget(); >- int oldLineHeight = lineHeight; >- >- super.setFont(font); >- initializeRenderer(); >- // keep the same top line visible. fixes 5815 >- if (lineHeight != oldLineHeight) { >- setVerticalScrollOffset(verticalScrollOffset * lineHeight / oldLineHeight, true); >- claimBottomFreeSpace(); >- } >- calculateContentWidth(); >- calculateScrollBars(); >- if (isBidi()) { >- caretDirection = SWT.NULL; >- createCaretBitmaps(); >- setBidiCaretDirection(); >- } >- else { >- Caret caret = getCaret(); >- if (caret != null) { >- caret.setSize(caret.getSize().x, lineHeight); >- } >- } >- // always set the caret location. Fixes 6685 >- setCaretLocation(); >- super.redraw(); >+ checkWidget(); >+ int oldLineHeight = lineHeight; >+ >+ super.setFont(font); >+ initializeRenderer(); >+ // keep the same top line visible. fixes 5815 >+ if (lineHeight != oldLineHeight) { >+ setVerticalScrollOffset(verticalScrollOffset * lineHeight / oldLineHeight, true); >+ claimBottomFreeSpace(); >+ } >+ calculateContentWidth(); >+ calculateScrollBars(); >+ if (isBidi()) { >+ caretDirection = SWT.NULL; >+ createCaretBitmaps(); >+ setBidiCaretDirection(); >+ } >+ else { >+ Caret caret = getCaret(); >+ if (caret != null) { >+ caret.setSize(caret.getSize().x, lineHeight); >+ } >+ } >+ // always set the caret location. Fixes 6685 >+ setCaretLocation(); >+ super.redraw(); > } > /** > * @see org.eclipse.swt.widgets.Control#setForeground > */ > public void setForeground(Color color) { >- checkWidget(); >- super.setForeground(color); >- foreground = color; >- redraw(); >+ checkWidget(); >+ super.setForeground(color); >+ foreground = color; >+ redraw(); > } >-/** >+/** > * Sets the horizontal scroll offset relative to the start of the line. > * Do nothing if there is no text set. > * <p> >- * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the >+ * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the > * widget. > * </p> > * >- * @param offset horizontal scroll offset relative to the start >- * of the line, measured in character increments starting at 0, if >- * equal to 0 the content is not scrolled, if > 0 = the content is scrolled. >+ * @param offset horizontal scroll offset relative to the start >+ * of the line, measured in character increments starting at 0, if >+ * equal to 0 the content is not scrolled, if > 0 = the content is scrolled. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > public void setHorizontalIndex(int offset) { >- checkWidget(); >- int clientAreaWidth = getClientArea().width; >- if (getCharCount() == 0) { >- return; >- } >- if (offset < 0) { >- offset = 0; >- } >- offset *= getHorizontalIncrement(); >- // allow any value if client area width is unknown or 0. >- // offset will be checked in resize handler. >- // don't use isVisible since width is known even if widget >- // is temporarily invisible >- if (clientAreaWidth > 0) { >- int width = lineCache.getWidth(); >- // prevent scrolling if the content fits in the client area. >- // align end of longest line with right border of client area >- // if offset is out of range. >- if (offset > width - clientAreaWidth) { >- offset = Math.max(0, width - clientAreaWidth); >- } >- } >- scrollHorizontalBar(offset - horizontalScrollOffset); >+ checkWidget(); >+ int clientAreaWidth = getClientArea().width; >+ if (getCharCount() == 0) { >+ return; >+ } >+ if (offset < 0) { >+ offset = 0; >+ } >+ offset *= getHorizontalIncrement(); >+ // allow any value if client area width is unknown or 0. >+ // offset will be checked in resize handler. >+ // don't use isVisible since width is known even if widget >+ // is temporarily invisible >+ if (clientAreaWidth > 0) { >+ int width = lineCache.getWidth(); >+ // prevent scrolling if the content fits in the client area. >+ // align end of longest line with right border of client area >+ // if offset is out of range. >+ if (offset > width - clientAreaWidth) { >+ offset = Math.max(0, width - clientAreaWidth); >+ } >+ } >+ scrollHorizontalBar(offset - horizontalScrollOffset); > } >-/** >+/** > * Sets the horizontal pixel offset relative to the start of the line. > * Do nothing if there is no text set. > * <p> >- * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text >+ * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text > * is set in the widget. > * </p> > * >- * @param pixel horizontal pixel offset relative to the start >- * of the line. >+ * @param pixel horizontal pixel offset relative to the start >+ * of the line. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> >@@ -7168,85 +7239,85 @@ > * @since 2.0 > */ > public void setHorizontalPixel(int pixel) { >- checkWidget(); >- int clientAreaWidth = getClientArea().width; >- if (getCharCount() == 0) { >- return; >- } >- if (pixel < 0) { >- pixel = 0; >- } >- // allow any value if client area width is unknown or 0. >- // offset will be checked in resize handler. >- // don't use isVisible since width is known even if widget >- // is temporarily invisible >- if (clientAreaWidth > 0) { >- int width = lineCache.getWidth(); >- // prevent scrolling if the content fits in the client area. >- // align end of longest line with right border of client area >- // if offset is out of range. >- if (pixel > width - clientAreaWidth) { >- pixel = Math.max(0, width - clientAreaWidth); >- } >- } >- scrollHorizontalBar(pixel - horizontalScrollOffset); >+ checkWidget(); >+ int clientAreaWidth = getClientArea().width; >+ if (getCharCount() == 0) { >+ return; >+ } >+ if (pixel < 0) { >+ pixel = 0; >+ } >+ // allow any value if client area width is unknown or 0. >+ // offset will be checked in resize handler. >+ // don't use isVisible since width is known even if widget >+ // is temporarily invisible >+ if (clientAreaWidth > 0) { >+ int width = lineCache.getWidth(); >+ // prevent scrolling if the content fits in the client area. >+ // align end of longest line with right border of client area >+ // if offset is out of range. >+ if (pixel > width - clientAreaWidth) { >+ pixel = Math.max(0, width - clientAreaWidth); >+ } >+ } >+ scrollHorizontalBar(pixel - horizontalScrollOffset); > } > /** >- * Adjusts the maximum and the page size of the horizontal scroll bar >+ * Adjusts the maximum and the page size of the horizontal scroll bar > * to reflect content width changes. > */ > void setHorizontalScrollBar() { >- ScrollBar horizontalBar = getHorizontalBar(); >- >- if (horizontalBar != null && horizontalBar.getVisible()) { >- final int INACTIVE = 1; >- Rectangle clientArea = getClientArea(); >- // only set the real values if the scroll bar can be used >- // (ie. because the thumb size is less than the scroll maximum) >- // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92 >- if (clientArea.width < lineCache.getWidth()) { >- horizontalBar.setValues( >- horizontalBar.getSelection(), >- horizontalBar.getMinimum(), >- lineCache.getWidth(), // maximum >- clientArea.width - leftMargin - rightMargin, // thumb size >- horizontalBar.getIncrement(), >- clientArea.width - leftMargin - rightMargin); // page size >- } >- else >- if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) { >- horizontalBar.setValues( >- horizontalBar.getSelection(), >- horizontalBar.getMinimum(), >- INACTIVE, >- INACTIVE, >- horizontalBar.getIncrement(), >- INACTIVE); >- } >- } >+ ScrollBar horizontalBar = getHorizontalBar(); >+ >+ if (horizontalBar != null && horizontalBar.getVisible()) { >+ final int INACTIVE = 1; >+ Rectangle clientArea = getClientArea(); >+ // only set the real values if the scroll bar can be used >+ // (ie. because the thumb size is less than the scroll maximum) >+ // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92 >+ if (clientArea.width < lineCache.getWidth()) { >+ horizontalBar.setValues( >+ horizontalBar.getSelection(), >+ horizontalBar.getMinimum(), >+ lineCache.getWidth(), // maximum >+ clientArea.width - leftMargin - rightMargin, // thumb size >+ horizontalBar.getIncrement(), >+ clientArea.width - leftMargin - rightMargin); // page size >+ } >+ else >+ if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) { >+ horizontalBar.setValues( >+ horizontalBar.getSelection(), >+ horizontalBar.getMinimum(), >+ INACTIVE, >+ INACTIVE, >+ horizontalBar.getIncrement(), >+ INACTIVE); >+ } >+ } > } >-/** >+/** > * Sets the background color of the specified lines. > * The background color is drawn for the width of the widget. All > * line background colors are discarded when setText is called. >- * The text background color if defined in a StyleRange overlays the >- * line background color. Should not be called if a LineBackgroundListener >+ * The text background color if defined in a StyleRange overlays the >+ * line background color. Should not be called if a LineBackgroundListener > * has been set since the listener maintains the line backgrounds. > * <p> >- * Line background colors are maintained relative to the line text, not the >+ * Line background colors are maintained relative to the line text, not the > * line index that is specified in this method call. >- * During text changes, when entire lines are inserted or removed, the line >- * background colors that are associated with the lines after the change >- * will "move" with their respective text. An entire line is defined as >- * extending from the first character on a line to the last and including the >- * line delimiter. >+ * During text changes, when entire lines are inserted or removed, the line >+ * background colors that are associated with the lines after the change >+ * will "move" with their respective text. An entire line is defined as >+ * extending from the first character on a line to the last and including the >+ * line delimiter. > * </p> > * <p> >- * When two lines are joined by deleting a line delimiter, the top line >- * background takes precedence and the color of the bottom line is deleted. >- * For all other text changes line background colors will remain unchanged. >+ * When two lines are joined by deleting a line delimiter, the top line >+ * background takes precedence and the color of the bottom line is deleted. >+ * For all other text changes line background colors will remain unchanged. > * </p> >- * >+ * > * @param startLine first line the color is applied to, 0 based > * @param lineCount number of lines the color applies to. > * @param background line background color >@@ -7259,87 +7330,87 @@ > * </ul> > */ > public void setLineBackground(int startLine, int lineCount, Color background) { >- checkWidget(); >- int partialBottomIndex = getPartialBottomIndex(); >- >- // this API can not be used if the client is providing the line background >- if (userLineBackground) { >- return; >- } >- if (startLine < 0 || startLine + lineCount > logicalContent.getLineCount()) { >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- defaultLineStyler.setLineBackground(startLine, lineCount, background); >- // do nothing if redraw range is completely invisible >- if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) { >- return; >- } >- // only redraw visible lines >- if (startLine < topIndex) { >- lineCount -= topIndex - startLine; >- startLine = topIndex; >- } >- if (startLine + lineCount - 1 > partialBottomIndex) { >- lineCount = partialBottomIndex - startLine + 1; >- } >- startLine -= topIndex; >- super.redraw( >- leftMargin, startLine * lineHeight + topMargin, >- getClientArea().width - leftMargin - rightMargin, lineCount * lineHeight, true); >+ checkWidget(); >+ int partialBottomIndex = getPartialBottomIndex(); >+ >+ // this API can not be used if the client is providing the line background >+ if (userLineBackground) { >+ return; >+ } >+ if (startLine < 0 || startLine + lineCount > logicalContent.getLineCount()) { >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ defaultLineStyler.setLineBackground(startLine, lineCount, background); >+ // do nothing if redraw range is completely invisible >+ if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) { >+ return; >+ } >+ // only redraw visible lines >+ if (startLine < topIndex) { >+ lineCount -= topIndex - startLine; >+ startLine = topIndex; >+ } >+ if (startLine + lineCount - 1 > partialBottomIndex) { >+ lineCount = partialBottomIndex - startLine + 1; >+ } >+ startLine -= topIndex; >+ super.redraw( >+ leftMargin, startLine * lineHeight + topMargin, >+ getClientArea().width - leftMargin - rightMargin, lineCount * lineHeight, true); > } > /** > * Flips selection anchor based on word selection direction. > */ > void setMouseWordSelectionAnchor() { >- if (mouseDoubleClick == false) { >- return; >- } >- if (caretOffset < doubleClickSelection.x) { >- selectionAnchor = doubleClickSelection.y; >- } >- else >- if (caretOffset > doubleClickSelection.y) { >- selectionAnchor = doubleClickSelection.x; >- } >+ if (mouseDoubleClick == false) { >+ return; >+ } >+ if (caretOffset < doubleClickSelection.x) { >+ selectionAnchor = doubleClickSelection.y; >+ } >+ else >+ if (caretOffset > doubleClickSelection.y) { >+ selectionAnchor = doubleClickSelection.x; >+ } > } > /** >- * Adjusts the maximum and the page size of the scroll bars to >+ * Adjusts the maximum and the page size of the scroll bars to > * reflect content width/length changes. > */ > void setScrollBars() { >- ScrollBar verticalBar = getVerticalBar(); >- >- if (verticalBar != null) { >- Rectangle clientArea = getClientArea(); >- final int INACTIVE = 1; >- int maximum = content.getLineCount() * getVerticalIncrement(); >- >- // only set the real values if the scroll bar can be used >- // (ie. because the thumb size is less than the scroll maximum) >- // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92 >- if (clientArea.height < maximum) { >- verticalBar.setValues( >- verticalBar.getSelection(), >- verticalBar.getMinimum(), >- maximum, >- clientArea.height, // thumb size >- verticalBar.getIncrement(), >- clientArea.height); // page size >- } >- else >- if (verticalBar.getThumb() != INACTIVE || verticalBar.getMaximum() != INACTIVE) { >- verticalBar.setValues( >- verticalBar.getSelection(), >- verticalBar.getMinimum(), >- INACTIVE, >- INACTIVE, >- verticalBar.getIncrement(), >- INACTIVE); >- } >- } >- setHorizontalScrollBar(); >+ ScrollBar verticalBar = getVerticalBar(); >+ >+ if (verticalBar != null) { >+ Rectangle clientArea = getClientArea(); >+ final int INACTIVE = 1; >+ int maximum = content.getLineCount() * getVerticalIncrement(); >+ >+ // only set the real values if the scroll bar can be used >+ // (ie. because the thumb size is less than the scroll maximum) >+ // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92 >+ if (clientArea.height < maximum) { >+ verticalBar.setValues( >+ verticalBar.getSelection(), >+ verticalBar.getMinimum(), >+ maximum, >+ clientArea.height, // thumb size >+ verticalBar.getIncrement(), >+ clientArea.height); // page size >+ } >+ else >+ if (verticalBar.getThumb() != INACTIVE || verticalBar.getMaximum() != INACTIVE) { >+ verticalBar.setValues( >+ verticalBar.getSelection(), >+ verticalBar.getMinimum(), >+ INACTIVE, >+ INACTIVE, >+ verticalBar.getIncrement(), >+ INACTIVE); >+ } >+ } >+ setHorizontalScrollBar(); > } >-/** >+/** > * Sets the selection to the given position and scrolls it into view. Equivalent to setSelection(start,start). > * <p> > * >@@ -7351,24 +7422,24 @@ > * </ul> > * @exception IllegalArgumentException <ul> > * <li>ERROR_INVALID_RANGE when start is outside the widget content >- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a >+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a > * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter) >- * </ul> >+ * </ul> > */ > public void setSelection(int start) { >- // checkWidget test done in setSelectionRange >- setSelection(start, start); >+ // checkWidget test done in setSelectionRange >+ setSelection(start, start); > } >-/** >+/** > * Sets the selection and scrolls it into view. > * <p> > * Indexing is zero based. Text selections are specified in terms of >- * caret positions. In a text widget that contains N characters, there are >+ * caret positions. In a text widget that contains N characters, there are > * N+1 caret positions, ranging from 0..N > * </p> > * > * @param point x=selection start offset, y=selection end offset >- * The caret will be placed at the selection start when x > y. >+ * The caret will be placed at the selection start when x > y. > * @see #setSelection(int,int) > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -7377,14 +7448,14 @@ > * @exception IllegalArgumentException <ul> > * <li>ERROR_NULL_ARGUMENT when point is null</li> > * <li>ERROR_INVALID_RANGE when start or end is outside the widget content >- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a >+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a > * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter) >- * </ul> >+ * </ul> > */ > public void setSelection(Point point) { >- checkWidget(); >- if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); >- setSelection(point.x, point.y); >+ checkWidget(); >+ if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); >+ setSelection(point.x, point.y); > } > /** > * Sets the receiver's selection background color to the color specified >@@ -7394,7 +7465,7 @@ > * @param color the new color (or null) > * > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> >+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> > * </ul> > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -7403,13 +7474,13 @@ > * @since 2.1 > */ > public void setSelectionBackground (Color color) { >- checkWidget (); >- if (color != null) { >- if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- selectionBackground = color; >- redraw(); >-} >+ checkWidget (); >+ if (color != null) { >+ if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ selectionBackground = color; >+ redraw(); >+} > /** > * Sets the receiver's selection foreground color to the color specified > * by the argument, or to the default system color for the control >@@ -7418,7 +7489,7 @@ > * @param color the new color (or null) > * > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> >+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> > * </ul> > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -7427,23 +7498,23 @@ > * @since 2.1 > */ > public void setSelectionForeground (Color color) { >- checkWidget (); >- if (color != null) { >- if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- selectionForeground = color; >- redraw(); >-} >-/** >+ checkWidget (); >+ if (color != null) { >+ if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ selectionForeground = color; >+ redraw(); >+} >+/** > * Sets the selection and scrolls it into view. > * <p> > * Indexing is zero based. Text selections are specified in terms of >- * caret positions. In a text widget that contains N characters, there are >+ * caret positions. In a text widget that contains N characters, there are > * N+1 caret positions, ranging from 0..N > * </p> > * >- * @param start selection start offset. The caret will be placed at the >- * selection start when start > end. >+ * @param start selection start offset. The caret will be placed at the >+ * selection start when start > end. > * @param end selection end offset > * @see #setSelectionRange(int,int) > * @exception SWTException <ul> >@@ -7452,100 +7523,144 @@ > * </ul> > * @exception IllegalArgumentException <ul> > * <li>ERROR_INVALID_RANGE when start or end is outside the widget content >- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a >+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a > * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter) > * </ul> > */ > public void setSelection(int start, int end) { >- // checkWidget test done in setSelectionRange >- setSelectionRange(start, end - start); >- showSelection(); >+ // checkWidget test done in setSelectionRange >+ setSelectionRange(start, end - start); >+ showSelection(); > } >-/** >- * Sets the selection. The new selection may not be visible. Call showSelection to scroll >+/** >+ * Sets the selection. The new selection may not be visible. Call showSelection to scroll > * the selection into view. > * <p> > * > * @param start offset of the first selected character, start >= 0 must be true. >- * @param length number of characters to select, 0 <= start + length <= getCharCount() >- * must be true. >- * A negative length places the caret at the visual start of the selection. >+ * @param length number of characters to select, 0 <= start + length <= getCharCount() >+ * must be true. >+ * A negative length places the caret at the visual start of the selection. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> > * <li>ERROR_INVALID_RANGE when the range specified by start and length is outside the widget content >- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a >+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a > * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter) > * </ul> > */ > public void setSelectionRange(int start, int length) { >- checkWidget(); >- int contentLength = getCharCount(); >- int end = start + length; >- >- if (start < 0 || end < 0 || start > contentLength || end > contentLength) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- if (isLineDelimiter(start) || isLineDelimiter(end)) { >- // the start offset or end offset of the selection range is inside a >- // multi byte line delimiter. This is an illegal operation and an exception >- // is thrown. Fixes 1GDKK3R >- SWT.error(SWT.ERROR_INVALID_ARGUMENT); >- } >- internalSetSelection(start, length, false); >- // always update the caret location. fixes 1G8FODP >- setCaretLocation(); >- if (isBidi()) { >- setBidiKeyboardLanguage(); >- } >-} >-/** >- * Sets the selection. >- * The new selection may not be visible. Call showSelection to scroll >+ checkWidget(); >+ int contentLength = getCharCount(); >+ int end = start + length; >+ >+ if (start < 0 || end < 0 || start > contentLength || end > contentLength) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ if (isLineDelimiter(start) || isLineDelimiter(end)) { >+ // the start offset or end offset of the selection range is inside a >+ // multi byte line delimiter. This is an illegal operation and an exception >+ // is thrown. Fixes 1GDKK3R >+ SWT.error(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ internalSetSelection(start, length, false); >+ // always update the caret location. fixes 1G8FODP >+ setCaretLocation(); >+ if (isBidi()) { >+ setBidiKeyboardLanguage(); >+ } >+} >+/** >+ * Sets the selection. >+ * The new selection may not be visible. Call showSelection to scroll > * the selection into view. > * <p> > * > * @param start offset of the first selected character, start >= 0 must be true. >- * @param length number of characters to select, 0 <= start + length >- * <= getCharCount() must be true. >- * A negative length places the caret at the selection start. >- * @param sendEvent a Selection event is sent when set to true and when >- * the selection is reset. >+ * @param length number of characters to select, 0 <= start + length >+ * <= getCharCount() must be true. >+ * A negative length places the caret at the selection start. >+ * @param sendEvent a Selection event is sent when set to true and when >+ * the selection is reset. > */ > void internalSetSelection(int start, int length, boolean sendEvent) { >- int end = start + length; >- >- if (start > end) { >- int temp = end; >- end = start; >- start = temp; >- } >- // is the selection range different or is the selection direction >- // different? >- if (selection.x != start || selection.y != end || >- (length > 0 && selectionAnchor != selection.x) || >- (length < 0 && selectionAnchor != selection.y)) { >- clearSelection(sendEvent); >- if (length < 0) { >- selectionAnchor = selection.y = end; >- caretOffset = selection.x = start; >- } >- else { >- selectionAnchor = selection.x = start; >- caretOffset = selection.y = end; >- } >- internalRedrawRange(selection.x, selection.y - selection.x, true); >- } >+ int end = start + length; >+ >+ if (start > end) { >+ int temp = end; >+ end = start; >+ start = temp; >+ } >+ // is the selection range different or is the selection direction >+ // different? >+ if (selection.x != start || selection.y != end || >+ (length > 0 && selectionAnchor != selection.x) || >+ (length < 0 && selectionAnchor != selection.y)) { >+ clearSelection(sendEvent); >+ if (length < 0) { >+ selectionAnchor = selection.y = end; >+ caretOffset = selection.x = start; >+ } >+ else { >+ selectionAnchor = selection.x = start; >+ caretOffset = selection.y = end; >+ } >+ internalRedrawRange(selection.x, selection.y - selection.x, true); >+ } >+ // see if we need to update the mouse pointer >+ internalSetCursor(); >+} >+/** >+ * Sets the mouse pointer based on whether it is over a selection and whether >+ * a custom cursor has been set. >+ */ >+void internalSetCursor() { >+ Point pos = this.toControl(getDisplay().getCursorLocation()); >+ internalSetCursor(pos.x, pos.y); >+} >+/** >+ * Sets the mouse pointer based on whether it is over a selection and whether >+ * a custom cursor has been set. >+ * >+ * @param x mouse x position in client coordinates >+ * @param y mouse y position in client coordinates >+ */ >+void internalSetCursor(int x, int y) { >+ int mouseLineIndex = (y + verticalScrollOffset) / lineHeight; >+ >+ if (mouseLineIndex < 0 || mouseLineIndex > content.getLineCount() - 1) { >+ // mouse is above or below content, set ibeam/custom cursor >+ Cursor cursor = (customCursor != null) ? customCursor : ibeamCursor; >+ if (!currentCursor.equals(cursor)) { >+ currentCursor = cursor; >+ super.setCursor(cursor); >+ } >+ } else { >+ if (isMouseOverSelection(x, y)) { >+ // mouse is over a selection, set arrow cursor >+ if (!currentCursor.equals(arrowCursor)) { >+ currentCursor = arrowCursor; >+ super.setCursor(arrowCursor); >+ } >+ } else { >+ Cursor cursor = (customCursor != null) ? customCursor : ibeamCursor; >+ // mouse is not over a selection, set ibeam/custom cursor >+ if (!currentCursor.equals(cursor)) { >+ currentCursor = cursor; >+ super.setCursor(cursor); >+ } >+ } >+ } > } >-/** >+/** > * Adds the specified style. The new style overwrites existing styles for the >- * specified range. Existing style ranges are adjusted if they partially >- * overlap with the new style, To clear an individual style, call setStyleRange >- * with a StyleRange that has null attributes. >+ * specified range. Existing style ranges are adjusted if they partially >+ * overlap with the new style, To clear an individual style, call setStyleRange >+ * with a StyleRange that has null attributes. > * <p> >- * Should not be called if a LineStyleListener has been set since the >+ * Should not be called if a LineStyleListener has been set since the > * listener maintains the styles. > * </p> > * >@@ -7557,84 +7672,84 @@ > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > * @exception IllegalArgumentException <ul> >- * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li> >+ * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li> > * </ul> > */ > public void setStyleRange(StyleRange range) { >- checkWidget(); >- >- // this API can not be used if the client is providing the line styles >- if (userLineStyle) { >- return; >- } >- // check the range, make sure it falls within the range of the >- // text >- if (range != null && range.start + range.length > content.getCharCount()) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- if (range != null) { >- boolean redrawFirstLine = false; >- boolean redrawLastLine = false; >- int firstLine = content.getLineAtOffset(range.start); >- int lastLine = content.getLineAtOffset(range.start + range.length); >- >- // if the style is not visible, there is no need to redraw >- boolean redrawLines = isAreaVisible(firstLine, lastLine); >- >- if (!redrawLines) { >- defaultLineStyler.setStyleRange(range); >- lineCache.reset(firstLine, lastLine - firstLine + 1, true); >- } else { >- // the first and last line needs to be redrawn completely if the >- // font style is changing from SWT.NORMAL to something else or >- // vice versa. fixes 1G7M5WE. >- int firstLineOffset = content.getOffsetAtLine(firstLine); >- int lastLineOffset = content.getOffsetAtLine(lastLine); >- if (isBidi()) { >- if (firstLine != lastLine) { >- redrawFirstLine = true; >- } >- redrawLastLine = true; >- } else { >- redrawFirstLine = isRedrawFirstLine(new StyleRange[] {range}, firstLine, firstLineOffset); >- if (lastLine != firstLine) { >- redrawLastLine = isRedrawLastLine(new StyleRange[] {range}, lastLine, lastLineOffset); >- } >- } >- defaultLineStyler.setStyleRange(range); >- // reset all lines affected by the style change but let the redraw >- // recalculate only those that are visible. >- lineCache.reset(firstLine, lastLine - firstLine + 1, true); >- internalRedrawRange(range.start, range.length, true); >- if (redrawFirstLine) { >- redrawLine(firstLine, range.start - firstLineOffset); >- } >- if (redrawLastLine) { >- redrawLine(lastLine, 0); >- } >- } >- } else { >- // clearing all styles >- defaultLineStyler.setStyleRange(range); >- lineCache.reset(0, content.getLineCount(), false); >- redraw(); >- } >- >- // make sure that the caret is positioned correctly. >- // caret location may change if font style changes. >- // fixes 1G8FODP >- setCaretLocation(); >+ checkWidget(); >+ >+ // this API can not be used if the client is providing the line styles >+ if (userLineStyle) { >+ return; >+ } >+ // check the range, make sure it falls within the range of the >+ // text >+ if (range != null && range.start + range.length > content.getCharCount()) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ if (range != null) { >+ boolean redrawFirstLine = false; >+ boolean redrawLastLine = false; >+ int firstLine = content.getLineAtOffset(range.start); >+ int lastLine = content.getLineAtOffset(range.start + range.length); >+ >+ // if the style is not visible, there is no need to redraw >+ boolean redrawLines = isAreaVisible(firstLine, lastLine); >+ >+ if (!redrawLines) { >+ defaultLineStyler.setStyleRange(range); >+ lineCache.reset(firstLine, lastLine - firstLine + 1, true); >+ } else { >+ // the first and last line needs to be redrawn completely if the >+ // font style is changing from SWT.NORMAL to something else or >+ // vice versa. fixes 1G7M5WE. >+ int firstLineOffset = content.getOffsetAtLine(firstLine); >+ int lastLineOffset = content.getOffsetAtLine(lastLine); >+ if (isBidi()) { >+ if (firstLine != lastLine) { >+ redrawFirstLine = true; >+ } >+ redrawLastLine = true; >+ } else { >+ redrawFirstLine = isRedrawFirstLine(new StyleRange[] {range}, firstLine, firstLineOffset); >+ if (lastLine != firstLine) { >+ redrawLastLine = isRedrawLastLine(new StyleRange[] {range}, lastLine, lastLineOffset); >+ } >+ } >+ defaultLineStyler.setStyleRange(range); >+ // reset all lines affected by the style change but let the redraw >+ // recalculate only those that are visible. >+ lineCache.reset(firstLine, lastLine - firstLine + 1, true); >+ internalRedrawRange(range.start, range.length, true); >+ if (redrawFirstLine) { >+ redrawLine(firstLine, range.start - firstLineOffset); >+ } >+ if (redrawLastLine) { >+ redrawLine(lastLine, 0); >+ } >+ } >+ } else { >+ // clearing all styles >+ defaultLineStyler.setStyleRange(range); >+ lineCache.reset(0, content.getLineCount(), false); >+ redraw(); >+ } >+ >+ // make sure that the caret is positioned correctly. >+ // caret location may change if font style changes. >+ // fixes 1G8FODP >+ setCaretLocation(); > } >-/** >- * Sets styles to be used for rendering the widget content. All styles >+/** >+ * Sets styles to be used for rendering the widget content. All styles > * in the widget will be replaced with the given set of styles. > * <p> >- * Should not be called if a LineStyleListener has been set since the >+ * Should not be called if a LineStyleListener has been set since the > * listener maintains the styles. > * </p> > * > * @param ranges StyleRange objects containing the style information. >- * The ranges should not overlap. The style rendering is undefined if >+ * The ranges should not overlap. The style rendering is undefined if > * the ranges do overlap. Must not be null. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> >@@ -7642,45 +7757,45 @@ > * </ul> > * @exception IllegalArgumentException <ul> > * <li>ERROR_NULL_ARGUMENT when listener is null</li> >- * <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li> >+ * <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li> > * </ul> > */ > public void setStyleRanges(StyleRange[] ranges) { >- checkWidget(); >- // this API can not be used if the client is providing the line styles >- if (userLineStyle) { >- return; >- } >- if (ranges == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- // check the last range, make sure it falls within the range of the >- // current text >- if (ranges.length != 0) { >- StyleRange last = ranges[ranges.length-1]; >- int lastEnd = last.start + last.length; >- int firstLine = content.getLineAtOffset(ranges[0].start); >- int lastLine; >- if (lastEnd > content.getCharCount()) { >- SWT.error(SWT.ERROR_INVALID_RANGE); >- } >- lastLine = content.getLineAtOffset(lastEnd); >- // reset all lines affected by the style change >- lineCache.reset(firstLine, lastLine - firstLine + 1, true); >- } >- else { >- // reset all lines >- lineCache.reset(0, content.getLineCount(), false); >- } >- defaultLineStyler.setStyleRanges(ranges); >- redraw(); // should only redraw affected area to avoid flashing >- // make sure that the caret is positioned correctly. >- // caret location may change if font style changes. >- // fixes 1G8FODP >- setCaretLocation(); >+ checkWidget(); >+ // this API can not be used if the client is providing the line styles >+ if (userLineStyle) { >+ return; >+ } >+ if (ranges == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ // check the last range, make sure it falls within the range of the >+ // current text >+ if (ranges.length != 0) { >+ StyleRange last = ranges[ranges.length-1]; >+ int lastEnd = last.start + last.length; >+ int firstLine = content.getLineAtOffset(ranges[0].start); >+ int lastLine; >+ if (lastEnd > content.getCharCount()) { >+ SWT.error(SWT.ERROR_INVALID_RANGE); >+ } >+ lastLine = content.getLineAtOffset(lastEnd); >+ // reset all lines affected by the style change >+ lineCache.reset(firstLine, lastLine - firstLine + 1, true); >+ } >+ else { >+ // reset all lines >+ lineCache.reset(0, content.getLineCount(), false); >+ } >+ defaultLineStyler.setStyleRanges(ranges); >+ redraw(); // should only redraw affected area to avoid flashing >+ // make sure that the caret is positioned correctly. >+ // caret location may change if font style changes. >+ // fixes 1G8FODP >+ setCaretLocation(); > } >-/** >- * Sets the tab width. >+/** >+ * Sets the tab width. > * <p> > * > * @param tabs tab width measured in characters. >@@ -7690,37 +7805,37 @@ > * </ul> > */ > public void setTabs(int tabs) { >- checkWidget(); >- tabLength = tabs; >- renderer.setTabLength(tabLength); >- if (caretOffset > 0) { >- caretOffset = 0; >- if (isBidi()) { >- showBidiCaret(); >- } >- else { >- showCaret(); >- } >- clearSelection(false); >- } >- // reset all line widths when the tab width changes >- lineCache.reset(0, content.getLineCount(), false); >- redraw(); >-} >-/** >- * Sets the widget content. >- * If the widget has the SWT.SINGLE style and "text" contains more than >- * one line, only the first line is rendered but the text is stored >- * unchanged. A subsequent call to getText will return the same text >+ checkWidget(); >+ tabLength = tabs; >+ renderer.setTabLength(tabLength); >+ if (caretOffset > 0) { >+ caretOffset = 0; >+ if (isBidi()) { >+ showBidiCaret(); >+ } >+ else { >+ showCaret(); >+ } >+ clearSelection(false); >+ } >+ // reset all line widths when the tab width changes >+ lineCache.reset(0, content.getLineCount(), false); >+ redraw(); >+} >+/** >+ * Sets the widget content. >+ * If the widget has the SWT.SINGLE style and "text" contains more than >+ * one line, only the first line is rendered but the text is stored >+ * unchanged. A subsequent call to getText will return the same text > * that was set. > * <p> >- * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE >+ * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE > * style is used. > * </p> > * >- * @param text new widget content. Replaces existing content. Line styles >- * that were set using StyledText API are discarded. The >- * current selection is also discarded. >+ * @param text new widget content. Replaces existing content. Line styles >+ * that were set using StyledText API are discarded. The >+ * current selection is also discarded. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> >@@ -7730,32 +7845,32 @@ > * </ul> > */ > public void setText(String text) { >- checkWidget(); >- Event event = new Event(); >- >- if (text == null) { >- SWT.error(SWT.ERROR_NULL_ARGUMENT); >- } >- event.start = 0; >- event.end = getCharCount(); >- event.text = text; >- event.doit = true; >- notifyListeners(SWT.Verify, event); >- if (event.doit) { >- StyledTextEvent styledTextEvent = null; >- >- if (isListening(ExtendedModify)) { >- styledTextEvent = new StyledTextEvent(logicalContent); >- styledTextEvent.start = event.start; >- styledTextEvent.end = event.start + event.text.length(); >- styledTextEvent.text = content.getTextRange(event.start, event.end - event.start); >- } >- content.setText(event.text); >- notifyListeners(SWT.Modify, event); >- if (styledTextEvent != null) { >- notifyListeners(ExtendedModify, styledTextEvent); >- } >- } >+ checkWidget(); >+ Event event = new Event(); >+ >+ if (text == null) { >+ SWT.error(SWT.ERROR_NULL_ARGUMENT); >+ } >+ event.start = 0; >+ event.end = getCharCount(); >+ event.text = text; >+ event.doit = true; >+ notifyListeners(SWT.Verify, event); >+ if (event.doit) { >+ StyledTextEvent styledTextEvent = null; >+ >+ if (isListening(ExtendedModify)) { >+ styledTextEvent = new StyledTextEvent(logicalContent); >+ styledTextEvent.start = event.start; >+ styledTextEvent.end = event.start + event.text.length(); >+ styledTextEvent.text = content.getTextRange(event.start, event.end - event.start); >+ } >+ content.setText(event.text); >+ notifyListeners(SWT.Modify, event); >+ if (styledTextEvent != null) { >+ notifyListeners(ExtendedModify, styledTextEvent); >+ } >+ } > } > /** > * Sets the text limit. >@@ -7774,50 +7889,50 @@ > * </ul> > */ > public void setTextLimit(int limit) { >- checkWidget(); >- if (limit == 0) { >- SWT.error(SWT.ERROR_CANNOT_BE_ZERO); >- } >- textLimit = limit; >+ checkWidget(); >+ if (limit == 0) { >+ SWT.error(SWT.ERROR_CANNOT_BE_ZERO); >+ } >+ textLimit = limit; > } > /** > * Sets the top index. Do nothing if there is no text set. > * <p> >- * The top index is the index of the line that is currently at the top >+ * The top index is the index of the line that is currently at the top > * of the widget. The top index changes when the widget is scrolled. > * Indexing starts from zero. > * Note: The top index is reset to 0 when new text is set in the widget. > * </p> > * >- * @param index new top index. Must be between 0 and >- * getLineCount() - fully visible lines per page. If no lines are fully >- * visible the maximum value is getLineCount() - 1. An out of range >- * index will be adjusted accordingly. >+ * @param index new top index. Must be between 0 and >+ * getLineCount() - fully visible lines per page. If no lines are fully >+ * visible the maximum value is getLineCount() - 1. An out of range >+ * index will be adjusted accordingly. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> > * </ul> > */ > public void setTopIndex(int topIndex) { >- checkWidget(); >- int lineCount = logicalContent.getLineCount(); >- int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole())); >- >- if (getCharCount() == 0) { >- return; >- } >- if (topIndex < 0) { >- topIndex = 0; >- } >- else >- if (topIndex > lineCount - pageSize) { >- topIndex = lineCount - pageSize; >- } >- if (wordWrap) { >- int logicalLineOffset = logicalContent.getOffsetAtLine(topIndex); >- topIndex = content.getLineAtOffset(logicalLineOffset); >- } >- setVerticalScrollOffset(topIndex * getVerticalIncrement(), true); >+ checkWidget(); >+ int lineCount = logicalContent.getLineCount(); >+ int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole())); >+ >+ if (getCharCount() == 0) { >+ return; >+ } >+ if (topIndex < 0) { >+ topIndex = 0; >+ } >+ else >+ if (topIndex > lineCount - pageSize) { >+ topIndex = lineCount - pageSize; >+ } >+ if (wordWrap) { >+ int logicalLineOffset = logicalContent.getOffsetAtLine(topIndex); >+ topIndex = content.getLineAtOffset(logicalLineOffset); >+ } >+ setVerticalScrollOffset(topIndex * getVerticalIncrement(), true); > } > /** > * Sets the top pixel offset. Do nothing if there is no text set. >@@ -7828,9 +7943,9 @@ > * Note: The top pixel is reset to 0 when new text is set in the widget. > * </p> > * >- * @param pixel new top pixel offset. Must be between 0 and >- * (getLineCount() - visible lines per page) / getLineHeight()). An out >- * of range offset will be adjusted accordingly. >+ * @param pixel new top pixel offset. Must be between 0 and >+ * (getLineCount() - visible lines per page) / getLineHeight()). An out >+ * of range offset will be adjusted accordingly. > * @exception SWTException <ul> > * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> > * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> >@@ -7838,130 +7953,130 @@ > * @since 2.0 > */ > public void setTopPixel(int pixel) { >- checkWidget(); >- int lineCount =content.getLineCount(); >- int height = getClientArea().height; >- int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height); >- >- if (getCharCount() == 0) { >- return; >- } >- if (pixel < 0) { >- pixel = 0; >- } >- else >- if (pixel > maxTopPixel) { >- pixel = maxTopPixel; >- } >- setVerticalScrollOffset(pixel, true); >+ checkWidget(); >+ int lineCount =content.getLineCount(); >+ int height = getClientArea().height; >+ int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height); >+ >+ if (getCharCount() == 0) { >+ return; >+ } >+ if (pixel < 0) { >+ pixel = 0; >+ } >+ else >+ if (pixel > maxTopPixel) { >+ pixel = maxTopPixel; >+ } >+ setVerticalScrollOffset(pixel, true); > } > /** > * Scrolls the widget vertically. > * <p> > * > * @param pixelOffset the new vertical scroll offset >- * @param adjustScrollBar >- * true= the scroll thumb will be moved to reflect the new scroll offset. >- * false = the scroll thumb will not be moved >- * @return >- * true=the widget was scrolled >- * false=the widget was not scrolled, the given offset is not valid. >+ * @param adjustScrollBar >+ * true= the scroll thumb will be moved to reflect the new scroll offset. >+ * false = the scroll thumb will not be moved >+ * @return >+ * true=the widget was scrolled >+ * false=the widget was not scrolled, the given offset is not valid. > */ > boolean setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) { >- Rectangle clientArea; >- ScrollBar verticalBar = getVerticalBar(); >- >- if (pixelOffset == verticalScrollOffset) { >- return false; >- } >- if (verticalBar != null && adjustScrollBar) { >- verticalBar.setSelection(pixelOffset); >- } >- clientArea = getClientArea(); >- scroll( >- 0, 0, // destination x, y >- 0, pixelOffset - verticalScrollOffset, // source x, y >- clientArea.width, clientArea.height, true); >- >- verticalScrollOffset = pixelOffset; >- calculateTopIndex(); >- setCaretLocation(); >- return true; >+ Rectangle clientArea; >+ ScrollBar verticalBar = getVerticalBar(); >+ >+ if (pixelOffset == verticalScrollOffset) { >+ return false; >+ } >+ if (verticalBar != null && adjustScrollBar) { >+ verticalBar.setSelection(pixelOffset); >+ } >+ clientArea = getClientArea(); >+ scroll( >+ 0, 0, // destination x, y >+ 0, pixelOffset - verticalScrollOffset, // source x, y >+ clientArea.width, clientArea.height, true); >+ >+ verticalScrollOffset = pixelOffset; >+ calculateTopIndex(); >+ setCaretLocation(); >+ return true; > } > /** > * Scrolls the specified location into view. > * <p> >- * >+ * > * @param x the x coordinate that should be made visible. > * @param line the line that should be made visible. Relative to the >- * first line in the document. >- * @return >- * true=the widget was scrolled to make the specified location visible. >- * false=the specified location is already visible, the widget was >- * not scrolled. >+ * first line in the document. >+ * @return >+ * true=the widget was scrolled to make the specified location visible. >+ * false=the specified location is already visible, the widget was >+ * not scrolled. > */ > boolean showLocation(int x, int line) { >- int clientAreaWidth = getClientArea().width - leftMargin - rightMargin; >- int verticalIncrement = getVerticalIncrement(); >- int horizontalIncrement = clientAreaWidth / 4; >- boolean scrolled = false; >- >- if (x < leftMargin) { >- // always make 1/4 of a page visible >- x = Math.max(horizontalScrollOffset * -1, x - horizontalIncrement); >- scrolled = scrollHorizontalBar(x); >- } >- else >- if (x >= clientAreaWidth) { >- // always make 1/4 of a page visible >- x = Math.min(lineCache.getWidth() - horizontalScrollOffset, x + horizontalIncrement); >- scrolled = scrollHorizontalBar(x - clientAreaWidth); >- } >- if (line < topIndex) { >- scrolled = setVerticalScrollOffset(line * verticalIncrement, true); >- } >- else >- if (line > getBottomIndex()) { >- scrolled = setVerticalScrollOffset((line + 1) * verticalIncrement - getClientArea().height, true); >- } >- return scrolled; >+ int clientAreaWidth = getClientArea().width - leftMargin - rightMargin; >+ int verticalIncrement = getVerticalIncrement(); >+ int horizontalIncrement = clientAreaWidth / 4; >+ boolean scrolled = false; >+ >+ if (x < leftMargin) { >+ // always make 1/4 of a page visible >+ x = Math.max(horizontalScrollOffset * -1, x - horizontalIncrement); >+ scrolled = scrollHorizontalBar(x); >+ } >+ else >+ if (x >= clientAreaWidth) { >+ // always make 1/4 of a page visible >+ x = Math.min(lineCache.getWidth() - horizontalScrollOffset, x + horizontalIncrement); >+ scrolled = scrollHorizontalBar(x - clientAreaWidth); >+ } >+ if (line < topIndex) { >+ scrolled = setVerticalScrollOffset(line * verticalIncrement, true); >+ } >+ else >+ if (line > getBottomIndex()) { >+ scrolled = setVerticalScrollOffset((line + 1) * verticalIncrement - getClientArea().height, true); >+ } >+ return scrolled; > } > /** > * Sets the caret location and scrolls the caret offset into view. > */ > void showCaret() { >- int caretLine = content.getLineAtOffset(caretOffset); >- >- showCaret(caretLine); >+ int caretLine = content.getLineAtOffset(caretOffset); >+ >+ showCaret(caretLine); > } > /** > * Sets the caret location and scrolls the caret offset into view. > */ > void showCaret(int caretLine) { >- int lineOffset = content.getOffsetAtLine(caretLine); >- String lineText = content.getLine(caretLine); >- int offsetInLine = caretOffset - lineOffset; >- int xAtOffset = getXAtOffset(lineText, caretLine, offsetInLine); >- boolean scrolled = showLocation(xAtOffset, caretLine); >- boolean setWrapCaretLocation = false; >- Caret caret = getCaret(); >- >- if (wordWrap && caret != null) { >- int caretY = caret.getLocation().y; >- if ((caretY + verticalScrollOffset) / getVerticalIncrement() - 1 != caretLine) { >- setWrapCaretLocation = true; >- } >- } >- if (scrolled == false || setWrapCaretLocation) { >- // set the caret location if a scroll operation did not set it (as a >- // sideeffect of scrolling) or when in word wrap mode and the caret >- // line was explicitly specified (i.e., because getWrapCaretLine does >- // not return the desired line causing scrolling to not set it correctly) >- setCaretLocation(xAtOffset, caretLine); >- } >- if (isBidi()) { >- setBidiKeyboardLanguage(); >- } >+ int lineOffset = content.getOffsetAtLine(caretLine); >+ String lineText = content.getLine(caretLine); >+ int offsetInLine = caretOffset - lineOffset; >+ int xAtOffset = getXAtOffset(lineText, caretLine, offsetInLine); >+ boolean scrolled = showLocation(xAtOffset, caretLine); >+ boolean setWrapCaretLocation = false; >+ Caret caret = getCaret(); >+ >+ if (wordWrap && caret != null) { >+ int caretY = caret.getLocation().y; >+ if ((caretY + verticalScrollOffset) / getVerticalIncrement() - 1 != caretLine) { >+ setWrapCaretLocation = true; >+ } >+ } >+ if (scrolled == false || setWrapCaretLocation) { >+ // set the caret location if a scroll operation did not set it (as a >+ // sideeffect of scrolling) or when in word wrap mode and the caret >+ // line was explicitly specified (i.e., because getWrapCaretLine does >+ // not return the desired line causing scrolling to not set it correctly) >+ setCaretLocation(xAtOffset, caretLine); >+ } >+ if (isBidi()) { >+ setBidiKeyboardLanguage(); >+ } > } > /** > * Scrolls the specified offset into view. >@@ -7970,13 +8085,13 @@ > * @param offset offset that should be scolled into view > */ > void showOffset(int offset) { >- int line = content.getLineAtOffset(offset); >- int lineOffset = content.getOffsetAtLine(line); >- int offsetInLine = offset - lineOffset; >- String lineText = content.getLine(line); >- int xAtOffset = getXAtOffset(lineText, line, offsetInLine); >- >- showLocation(xAtOffset, line); >+ int line = content.getLineAtOffset(offset); >+ int lineOffset = content.getOffsetAtLine(line); >+ int offsetInLine = offset - lineOffset; >+ String lineText = content.getLine(line); >+ int xAtOffset = getXAtOffset(lineText, line, offsetInLine); >+ >+ showLocation(xAtOffset, line); > } > /** > /** >@@ -7991,92 +8106,92 @@ > * </ul> > */ > public void showSelection() { >- checkWidget(); >- boolean selectionFits; >- int startOffset, startLine, startX, endOffset, endLine, endX, offsetInLine; >- >- // is selection from right-to-left? >- boolean rightToLeft = caretOffset == selection.x; >- >- if (rightToLeft) { >- startOffset = selection.y; >- endOffset = selection.x; >- } else { >- startOffset = selection.x; >- endOffset = selection.y; >- } >- >- // calculate the logical start and end values for the selection >- startLine = content.getLineAtOffset(startOffset); >- offsetInLine = startOffset - content.getOffsetAtLine(startLine); >- startX = getXAtOffset(content.getLine(startLine), startLine, offsetInLine); >- endLine = content.getLineAtOffset(endOffset); >- offsetInLine = endOffset - content.getOffsetAtLine(endLine); >- endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine); >- >- // can the selection be fully displayed within the widget's visible width? >- int w = getClientArea().width; >- if (rightToLeft) { >- selectionFits = startX - endX <= w; >- } else { >- selectionFits = endX - startX <= w; >- } >- >- if (selectionFits) { >- // show as much of the selection as possible by first showing >- // the start of the selection >- showLocation(startX, startLine); >- // endX value could change if showing startX caused a scroll to occur >- endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine); >- showLocation(endX, endLine); >- } else { >- // just show the end of the selection since the selection start >- // will not be visible >- showLocation(endX, endLine); >- } >+ checkWidget(); >+ boolean selectionFits; >+ int startOffset, startLine, startX, endOffset, endLine, endX, offsetInLine; >+ >+ // is selection from right-to-left? >+ boolean rightToLeft = caretOffset == selection.x; >+ >+ if (rightToLeft) { >+ startOffset = selection.y; >+ endOffset = selection.x; >+ } else { >+ startOffset = selection.x; >+ endOffset = selection.y; >+ } >+ >+ // calculate the logical start and end values for the selection >+ startLine = content.getLineAtOffset(startOffset); >+ offsetInLine = startOffset - content.getOffsetAtLine(startLine); >+ startX = getXAtOffset(content.getLine(startLine), startLine, offsetInLine); >+ endLine = content.getLineAtOffset(endOffset); >+ offsetInLine = endOffset - content.getOffsetAtLine(endLine); >+ endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine); >+ >+ // can the selection be fully displayed within the widget's visible width? >+ int w = getClientArea().width; >+ if (rightToLeft) { >+ selectionFits = startX - endX <= w; >+ } else { >+ selectionFits = endX - startX <= w; >+ } >+ >+ if (selectionFits) { >+ // show as much of the selection as possible by first showing >+ // the start of the selection >+ showLocation(startX, startLine); >+ // endX value could change if showing startX caused a scroll to occur >+ endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine); >+ showLocation(endX, endLine); >+ } else { >+ // just show the end of the selection since the selection start >+ // will not be visible >+ showLocation(endX, endLine); >+ } > } > /** >- * Updates the caret direction when a delete operation occured based on >- * the type of the delete operation (next/previous character) and the >+ * Updates the caret direction when a delete operation occured based on >+ * the type of the delete operation (next/previous character) and the > * caret location (at a direction boundary or inside a direction segment). > * The intent is to place the caret at the visual location where a > * character was deleted. > * <p> >- * >- * @param isBackspace true=the previous character was deleted, false=the >- * character next to the caret location was deleted >+ * >+ * @param isBackspace true=the previous character was deleted, false=the >+ * character next to the caret location was deleted > * @param isDirectionBoundary true=the caret is between a R2L and L2R segment, >- * false=the caret is within a direction segment >+ * false=the caret is within a direction segment > */ > void updateBidiDirection(boolean isBackspace, boolean isDirectionBoundary) { >- if (isDirectionBoundary) { >- if (isBackspace) { >- // Deleted previous character (backspace) at a direction boundary >- // Go to direction segment of deleted character >- lastCaretDirection = ST.COLUMN_NEXT; >- } >- else { >- // Deleted next character. Go to direction segment of deleted character >- lastCaretDirection = ST.COLUMN_PREVIOUS; >- } >- } >- else { >- if (isBackspace) { >- // Delete previous character inside direction segment (i.e., not at a direction boundary) >- lastCaretDirection = ST.COLUMN_PREVIOUS; >- } >- else { >- // Deleted next character. >- lastCaretDirection = ST.COLUMN_NEXT; >- } >- } >+ if (isDirectionBoundary) { >+ if (isBackspace) { >+ // Deleted previous character (backspace) at a direction boundary >+ // Go to direction segment of deleted character >+ lastCaretDirection = ST.COLUMN_NEXT; >+ } >+ else { >+ // Deleted next character. Go to direction segment of deleted character >+ lastCaretDirection = ST.COLUMN_PREVIOUS; >+ } >+ } >+ else { >+ if (isBackspace) { >+ // Delete previous character inside direction segment (i.e., not at a direction boundary) >+ lastCaretDirection = ST.COLUMN_PREVIOUS; >+ } >+ else { >+ // Deleted next character. >+ lastCaretDirection = ST.COLUMN_NEXT; >+ } >+ } > } > /** > * Updates the selection and caret position depending on the text change. >- * If the selection intersects with the replaced text, the selection is >+ * If the selection intersects with the replaced text, the selection is > * reset and the caret moved to the end of the new text. > * If the selection is behind the replaced text it is moved so that the >- * same text remains selected. If the selection is before the replaced text >+ * same text remains selected. If the selection is before the replaced text > * it is left unchanged. > * <p> > * >@@ -8085,86 +8200,86 @@ > * @param newLength length of new text > */ > void updateSelection(int startOffset, int replacedLength, int newLength) { >- if (selection.y <= startOffset) { >- // selection ends before text change >- return; >- } >- if (selection.x < startOffset) { >- // clear selection fragment before text change >- internalRedrawRange(selection.x, startOffset - selection.x, true); >- } >- if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) { >- // clear selection fragment after text change. >- // do this only when the selection is actually affected by the >- // change. Selection is only affected if it intersects the change (1GDY217). >- int netNewLength = newLength - replacedLength; >- int redrawStart = startOffset + newLength; >- internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart, true); >- } >- if (selection.y > startOffset && selection.x < startOffset + replacedLength) { >- // selection intersects replaced text. set caret behind text change >- internalSetSelection(startOffset + newLength, 0, true); >- // always update the caret location. fixes 1G8FODP >- setCaretLocation(); >- } >- else { >- // move selection to keep same text selected >- internalSetSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true); >- // always update the caret location. fixes 1G8FODP >- setCaretLocation(); >- } >+ if (selection.y <= startOffset) { >+ // selection ends before text change >+ return; >+ } >+ if (selection.x < startOffset) { >+ // clear selection fragment before text change >+ internalRedrawRange(selection.x, startOffset - selection.x, true); >+ } >+ if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) { >+ // clear selection fragment after text change. >+ // do this only when the selection is actually affected by the >+ // change. Selection is only affected if it intersects the change (1GDY217). >+ int netNewLength = newLength - replacedLength; >+ int redrawStart = startOffset + newLength; >+ internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart, true); >+ } >+ if (selection.y > startOffset && selection.x < startOffset + replacedLength) { >+ // selection intersects replaced text. set caret behind text change >+ internalSetSelection(startOffset + newLength, 0, true); >+ // always update the caret location. fixes 1G8FODP >+ setCaretLocation(); >+ } >+ else { >+ // move selection to keep same text selected >+ internalSetSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true); >+ // always update the caret location. fixes 1G8FODP >+ setCaretLocation(); >+ } > } > /** > * Rewraps all lines > * <p> >- * >- * @param oldClientAreaWidth client area width before resize >- * occurred >+ * >+ * @param oldClientAreaWidth client area width before resize >+ * occurred > */ > void wordWrapResize(int oldClientAreaWidth) { >- WrappedContent wrappedContent = (WrappedContent) content; >- int newTopIndex; >+ WrappedContent wrappedContent = (WrappedContent) content; >+ int newTopIndex; > >- // all lines are wrapped and no rewrap required if widget has already >- // been visible, client area is now wider and visual (wrapped) line >- // count equals logical line count. >- if (oldClientAreaWidth != 0 && clientAreaWidth > oldClientAreaWidth && >- wrappedContent.getLineCount() == logicalContent.getLineCount()) { >- return; >- } >+ // all lines are wrapped and no rewrap required if widget has already >+ // been visible, client area is now wider and visual (wrapped) line >+ // count equals logical line count. >+ if (oldClientAreaWidth != 0 && clientAreaWidth > oldClientAreaWidth && >+ wrappedContent.getLineCount() == logicalContent.getLineCount()) { >+ return; >+ } > wrappedContent.wrapLines(); >- >+ > // adjust the top index so that top line remains the same >- newTopIndex = content.getLineAtOffset(topOffset); >- // topOffset is the beginning of the top line. therefore it >- // needs to be adjusted because in a wrapped line this is also >- // the end of the preceeding line. >- if (newTopIndex < content.getLineCount() - 1 && >- topOffset == content.getOffsetAtLine(newTopIndex + 1)) { >- newTopIndex++; >- } >+ newTopIndex = content.getLineAtOffset(topOffset); >+ // topOffset is the beginning of the top line. therefore it >+ // needs to be adjusted because in a wrapped line this is also >+ // the end of the preceeding line. >+ if (newTopIndex < content.getLineCount() - 1 && >+ topOffset == content.getOffsetAtLine(newTopIndex + 1)) { >+ newTopIndex++; >+ } > if (newTopIndex != topIndex) { >- ScrollBar verticalBar = getVerticalBar(); >- // adjust index and pixel offset manually instead of calling >- // setVerticalScrollOffset because the widget does not actually need >- // to be scrolled. causes flash otherwise. >- verticalScrollOffset += (newTopIndex - topIndex) * getVerticalIncrement(); >- // verticalScrollOffset may become negative if first line was >- // partially visible and second line was top line. prevent this from >- // happening to fix 8503. >- if (verticalScrollOffset < 0) { >- verticalScrollOffset = 0; >- } >- topIndex = newTopIndex; >- topOffset = content.getOffsetAtLine(topIndex); >- if (verticalBar != null) { >- verticalBar.setSelection(verticalScrollOffset); >- } >+ ScrollBar verticalBar = getVerticalBar(); >+ // adjust index and pixel offset manually instead of calling >+ // setVerticalScrollOffset because the widget does not actually need >+ // to be scrolled. causes flash otherwise. >+ verticalScrollOffset += (newTopIndex - topIndex) * getVerticalIncrement(); >+ // verticalScrollOffset may become negative if first line was >+ // partially visible and second line was top line. prevent this from >+ // happening to fix 8503. >+ if (verticalScrollOffset < 0) { >+ verticalScrollOffset = 0; >+ } >+ topIndex = newTopIndex; >+ topOffset = content.getOffsetAtLine(topIndex); >+ if (verticalBar != null) { >+ verticalBar.setSelection(verticalScrollOffset); >+ } > } >- // caret may be on a different line after a rewrap. >- // call setCaretLocation after fixing vertical scroll offset. >- setCaretLocation(); >- // word wrap may have changed on one of the visible lines >+ // caret may be on a different line after a rewrap. >+ // call setCaretLocation after fixing vertical scroll offset. >+ setCaretLocation(); >+ // word wrap may have changed on one of the visible lines > super.redraw(); > } > }
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 19825
: 3148 |
130504