| <html> | |
| <head> | |
| <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> | |
| <title>Into the Deep End of the SWT StyledText Widget</title> | |
| <link rel="stylesheet" href="../../default_style.css"> | |
| </head> | |
| <body LINK="#0000ff" VLINK="#800080"> | |
| <div align="right"> <font face="Times New Roman, Times, serif" size="2">Copyright | |
| © 2001,2002 International Business Machines Corp.</font> | |
| <table border=0 cellspacing=0 cellpadding=2 width="100%"> | |
| <tr> | |
| <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF"> Eclipse | |
| Corner Article</font></font></b></td> | |
| </tr> | |
| </table> | |
| </div> | |
| <div align="left"> | |
| <h1><img src="Idea.jpg" height=86 width=120 align=CENTER></h1> | |
| </div> | |
| <p> </p> | |
| <h1 ALIGN="CENTER">Into the Deep End of the</h1> | |
| <h1 ALIGN="CENTER">SWT StyledText Widget</h1> | |
| <blockquote> | |
| <b>Summary</b> | |
| <p>The<i> StyledText</i> widget is a customizable widget that can be used to display and | |
| edit text with different colors and font styles. In this article we discuss why you might | |
| want to customize the <i>StyledText</i> widget and how you would do that. The article | |
| assumes you are familiar with the concepts presented in our other article, <a | |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">"Getting | |
| Your Feet Wet With the SWT StyledText Widget"</a>.</p> | |
| <p><b>By Lynne Kues and Knut Radloff, OTI</b> | |
| <br> | |
| October 17, 2001; updated September 18, 2002 for Eclipse 2.0</p> | |
| </blockquote> | |
| <hr width="100%"> | |
| <h2>Overview</h2> | |
| <p>The <i>StyledText</i> widget is designed as a pluggable widget. You can supply any or | |
| all of the following components in order to customize the widget to your needs: | |
| <ul> | |
| <li> | |
| <i>StyledTextContent</i> for storing and manipulating the widget’s text. The <i>StyledText</i> | |
| widget interacts with its <i>StyledTextContent</i> in order to access and update the text | |
| that is being displayed and edited in the widget. </li> | |
| <li> | |
| <i>LineStyleListener</i> for answering the style information that is associated with the | |
| widget’s text. When the <i>StyledText</i> widget displays a line of text, it will ask | |
| its <i>LineStyleListener</i> for the line’s style information. </li> | |
| <li> | |
| <i>LineBackgroundListener</i> for answering background colors for the lines that are | |
| displayed by the widget. When the <i>StyledText</i> widget displays a line, it will ask | |
| its <i>LineBackgroundListener</i> for the line’s background color. </li> | |
| </ul> | |
| <p>The <i>StyledText</i> widget supplies default implementations for all of the above | |
| objects. Therefore, you can supply any combination of the above components. For example, | |
| you could supply just a <i>StyledTextContent</i> implementation and use the <i>StyledText</i> | |
| widget’s default <i>LineStyleListener</i> and <i>LineBackgroundListener</i>.</p> | |
| <h2>Implementing a StyledTextContent</h2> | |
| <h3>StyledTextContent Applicability</h3> | |
| <p>One of the main reasons to provide a content implementation for the <i>StyledText</i> | |
| widget is to avoid data duplication. If the text that is to be displayed by the <i>StyledText</i> | |
| widget is already stored and maintained in an object, you can eliminate data duplication | |
| by providing a content implementation. </p> | |
| <p>Providing your own <i>StyledTextContent</i> implementation will also give you more | |
| flexibility since you will have direct access to the text content. For example, you may | |
| want to manipulate the text independently of the widget. </p> | |
| <h3>StyledTextContent Method Overview</h3> | |
| <p>The <i>StyledTextContent </i>interface defines the protocol that is used by the <i>StyledText</i> | |
| widget to get information about the text content it should display. The interface contains | |
| all the methods that must be implemented in order to supply your own content | |
| implementation. </p> | |
| <i> | |
| <p>void addTextChangeListener(TextChangeListener listener)</i> | |
| <dir> | |
| <p>This method is called by the <i>StyledText </i>widget in order to register itself as a | |
| listener for content changes. The <i>StyledTextContent</i> implementer must store <i>TextChangeListeners | |
| </i>and notify these listeners when content changes occur. Notification occurs in two | |
| steps. First a <i>TextChangingEvent</i> should be sent when text content is about to | |
| change and then a <i>TextChangedEvent</i> should be sent when the text content has | |
| changed. Notification should occur in the <b>replaceTextRange</b> method (discussed | |
| below).</p> | |
| </dir> | |
| <address> | |
| int getCharCount() | |
| </address> | |
| <dl> | |
| <dd>This method should return the number of characters in the text content. </dd> | |
| </dl> | |
| <i> | |
| <p>String getLine(int lineIndex)</i> | |
| <dir> | |
| <p>This method should return the line at the given <i>lineIndex</i> without the line | |
| delimiter. Line indexes are zero-based. For example:</p> | |
| <pre><tt> line delimiter = "\r\n" | |
| text content = "Line 1\r\nLine2\r\nLine 3" | |
| </tt><i>getLine(0)</i><tt> should answer "Line 1" | |
| </tt><i>getLine(2)</i><tt> should answer "Line 3"</tt></pre> | |
| </dir> | |
| <i> | |
| <p>int getLineAtOffset(int offset)</i> | |
| <dir> | |
| <p>This method should return the index of the line at the given character <i>offset</i>. | |
| Character offsets are zero-based. For example:</p> | |
| <pre><tt> line delimiter = "\n" | |
| text content = "Line 1\nLine2\nLine 3" | |
| </tt><i>getLineAtOffset(6)</i><tt> should answer 0 | |
| </tt><i>getLineAtOffset(7)</i><tt> should answer 1</tt></pre> | |
| </dir> | |
| <i> | |
| <p>int getLineCount()</i> | |
| <dir> | |
| <p>This method should return the number of lines in the text content. | |
| For example:</p> | |
| <pre><tt> line delimiter = "\n" | |
| text content = "Line 1\nLine2\nLine 3" | |
| </tt><i>getLineCount()</i><tt> should answer 3</tt></pre> | |
| </dir> | |
| <i> | |
| <p>String getLineDelimiter()</i> | |
| <dir> | |
| <p>This method should return the line delimiter that should be used when | |
| inserting new lines (i.e., via the keyboard) into the text content.</p> | |
| </dir> | |
| <i> | |
| <p>int getOffsetAtLine(int lineIndex)</i> | |
| <dir> | |
| <p>This method should return the character offset of the first character | |
| for the given <i>lineIndex</i>. Character offsets are zero-based. For example:</p> | |
| <pre><tt> line delimiter = "\r\n" | |
| text content = "Line 1\r\nLine2\r\nLine 3" | |
| </tt><i>getOffsetAtLine(1)</i><tt> should answer 8 | |
| </tt><i>getOffsetAtLine(2)</i><tt> should answer 15</tt></pre> | |
| </dir> | |
| <i> | |
| <p>String getTextRange(int start, int length)</i> | |
| <dir> | |
| <p>This method should return the text for the given range. The range begins | |
| at character offset <i>start</i> and continues <i>length</i> characters. For example:</p> | |
| <pre><tt> text content = "Line 1\r\nLine2\r\nLine 3" | |
| </tt><i>getTextRange(3,8)</i><tt> should answer "e 1\r\nLin"</tt></pre> | |
| </dir> | |
| <i> | |
| <p>void removeTextChangeListener(TextChangeListener listener)</i> | |
| <ul> | |
| <p>This method should remove a registered <i>TextChangeListener.</i> The | |
| listener will no longer be notified of content changes.</p> | |
| </ul> | |
| <i> | |
| <p>void replaceTextRange(int start, int length, String newText)</i> | |
| <dir> | |
| <p>This method should replace the given text range (beginning at offset <i>start</i> | |
| and continuing for <i>replaceLength</i> characters) with <i>newText</i>. Before | |
| the text update occurs, the <i>StyledTextContent</i> implementor should notify | |
| its registered <i>TextChangeListeners</i> of the pending change. Notification | |
| should occur via the <i>TextChangingEvent</i>.</p> | |
| <dir> <tt> | |
| <pre><font COLOR="#000080">public class</font> TextChangingEvent <font COLOR="#000080">extends</font> TypedEvent { | |
| <font | |
| COLOR="#000080"> public int</font> newCharCount; <font | |
| COLOR="#800000">// the length of the new text | |
| </font> <font | |
| COLOR="#000080">public int</font> newLineCount; <font COLOR="#800000">// the number of new lines to be inserted | |
| </font> <font | |
| COLOR="#000080">public</font> String newText; <font COLOR="#800000">// the text to be inserted | |
| </font> <font | |
| COLOR="#000080">public int</font> replacedCharCount; <font COLOR="#800000">// the length of the text to be replaced | |
| </font> <font | |
| COLOR="#000080">public int</font> replacedLineCount; <font COLOR="#800000">// the number of lines to be replaced | |
| </font> <font | |
| COLOR="#000080">public int</font> start; <font COLOR="#800000">// the start offset of the text to be replaced | |
| </font>}</pre> | |
| </tt></dir> | |
| <p>Note that <i>newLineCount<b> </b></i>and <i>replacedLineCount<b> | |
| </b></i>should indicate the number of lines in the text to be inserted and the number of lines | |
| in the text to be replaced, respectively. For example:</p> | |
| </dir> | |
| <div align="center"><center> | |
| <table BORDER="1" CELLSPACING="2" BORDERCOLOR="#000000" CELLPADDING="7" WIDTH="576"> | |
| <tr> | |
| <td WIDTH="22%" VALIGN="TOP"><p ALIGN="CENTER"><font FACE="Courier New" SIZE="2"><i>replaced | |
| text</i></font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><i><p ALIGN="CENTER">inserted | |
| text</i></font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><i><p ALIGN="CENTER">replacedLineCount</i></font></td> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><i><p ALIGN="CENTER">newLineCount</i></font></td> | |
| </tr> | |
| <tr> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">""</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n"</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">1</font></td> | |
| </tr> | |
| <tr> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n\n"</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"a"</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">2</font></td> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> | |
| </tr> | |
| <tr> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"a"</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n\n"</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">2</font></td> | |
| </tr> | |
| <tr> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n"</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">""</font></td> | |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">1</font></td> | |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> | |
| </tr> | |
| </table> | |
| </center></div> | |
| <dir> | |
| <p>After the text content is updated, the <i>StyledTextContent </i>implementor | |
| should notify its registered <i>TextChangeListeners</i> that the change is complete. | |
| Notification should occur via the <i>TextChangedEvent. </i>When the StyledText widget | |
| receives this event, it will update the widget to reflect the change. The <i>TextChangedEvent</i> | |
| does not contain any data since all the information about the change is included in the <i>TextChangingEvent</i>. | |
| </p> | |
| </dir> | |
| <p><em>void setText(String newText)</em><font FACE="Courier New" | |
| SIZE="2"> </font> | |
| <dir> | |
| <p>This method replaces the entire existing text with <i>newText</i>. Use | |
| this method to initially specify the text content.</p> | |
| </dir> | |
| <p>Note that the <i>StyledTextContent</i> implementation does not deal with | |
| text styles. It is only responsible for managing the text content. | |
| The <i>LineStyleListener</i> and <i>LineBackgroundListener </i>objects manage style information. When | |
| you implement a <i>StyledTextContent</i>, you are not required to implement a <i>LineStyleListener</i> | |
| or a <i>LineBackgroundListener</i>. <em>StyledText's</em> default implementations | |
| of <em>LineStyleListener</em> and <em>LineBackgroundListener</em> will not change style | |
| information when a content change occurs. Styles for existing text will remain as | |
| before the content change and new text will not be assigned any styles. If a content | |
| change requires style changes, you can use the <b>replaceStyleRanges</b> (new since Eclipse 2.0) and | |
| <b>setStyleRange</b> API to update the style information. When to update style information is | |
| discussed below in the "Text | |
| Content/Style Update Processing" section</p> | |
| <h3>StyledTextContent Rules</h3> | |
| <p>It is up to the <i>StyledTextContent</i> implementation to decide which | |
| line delimiters it will recognize in order to determine the lines that are represented in | |
| a text string. The default <i>StyledTextContent</i> implementation that is supplied with | |
| the <i>StyledText</i> widget recognizes any combination of "\n", | |
| "\r\n", and "\r" as line delimiters. Doing so ensures that strings | |
| created with different line delimiters are interpreted correctly. Note that the delimiter | |
| for a specific platform comes into play when new lines are inserted. When inserting text, | |
| you want the new lines to be created with the platform line delimiter so that copy and | |
| paste operations between platform applications work properly. You specify the delimiter to | |
| use when inserting new text via the <b>getLineDelimiter </b>method. </p> | |
| <p>As discussed above, when a text change occurs, the <i>StyledTextContent</i> | |
| needs to send a <i>TextChangingEvent</i> and a <i>TextChangedEvent</i> to notify the <i>StyledText</i> | |
| widget about the text change. Text changes can occur in one of three ways: | |
| <ol TYPE="A"> | |
| <li>via the <i>StyledText</i> widget because of user input (e.g., keyboard | |
| entry)</li> | |
| <li>via the <i>StyledText</i> widget because API was called (e.g., <b>replaceTextRange | |
| </b>or <b>paste</b>)</li> | |
| <li>via your <i>StyledTextContent</i> implementation (i.e., your application | |
| changes text independent of the <i>StyledText</i> widget)</li> | |
| </ol> | |
| <p>The following diagram indicates how these text changes occur within the <em>StyledText</em> | |
| widget. The boxes in gray are discussed in length in our <a | |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a>, | |
| "Getting Your Feet Wet With the SWT StyledText Widget". For this article, | |
| we will expand upon the processing that occurs in Step 4, "Text Content/Style Update".</p> | |
| <p ALIGN="CENTER"><img src="TextChanges.gif" width="420" height="995" | |
| alt="TextChanges.gif (12968 bytes)"></p> | |
| <div align="center"><b>Figure 1 – Text Change Processing</b> </div> | |
| <h4>Text Content/Style Update Processing </h4> | |
| <p>The Text Content/Style Update process begins with the <em>StyledText</em> | |
| widget or your application (for Case C) calling the<strong> replaceTextRange(int, int, | |
| String)</strong> method. This method is the time and place to send the text change | |
| events and to update your text content. How processing should occur is | |
| depicted in the diagram below. The light gray boxes represent the processing that your <i>StyledTextContent</i> | |
| implementation should perform, while the dark gray boxes represent the processing that | |
| occurs in the <i>StyledText</i> widget.</p> | |
| <font FACE="Courier New" SIZE="2"> | |
| <p ALIGN="CENTER"> <img src="TextUpdate.gif" width="607" height="796" | |
| alt="TextUpdate.gif (11618 bytes)"></p> | |
| </font> | |
| <div align="center"><b>Figure 2 – Text Content/Style Update Processing</b> </div> | |
| <p>The text content/style update process is started by sending the <i>TextChangingEvent</i> | |
| (1). In response to this event, the <i>StyledText</i> widget prepares for the text change | |
| by posting screen update operations for the area of the text change (2). After the <i>TextChangingEvent</i> | |
| is sent, the application should update its text content to reflect the change (3), apply | |
| style changes (4), and send the <i>TextChanged</i> event (5). When the <i>StyledText</i> | |
| widget receives the <i>TextChangedEvent</i> it will update itself in accordance with the | |
| change (e.g., scroll bars will be adjusted) and the text change will be visibly presented | |
| in the <i>StyledText</i> widget (6). If you don't supply a <i>LineStyleListener</i> | |
| as described below, part of the text change process will include updating the | |
| style ranges to accommodate the text change (occurs in Step 2). As discussed in the first article | |
| <a HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html"> | |
| "Getting Your Feet Wet With the SWT StyledText Widget"</a>, the StyledText widget maintains | |
| styles for existing text and does not apply styles for any new text. If the new text requires | |
| styles or the existing styles need to be modified, this should be done in Step 4. </p> | |
| <p>Note that the rules that apply to updating styles also apply to updating line background colors. If you do | |
| not supply a <i>LineBackgroundListener</i> as described below, part of the text change process will | |
| include updating line background colors if the text change includes adding or removing lines. As with styles, | |
| the <i>StyledText</i> widget maintains existing line background colors. If the line | |
| background colors need to be updated as a result of the text change, this should be done in Step 4. | |
| </p> | |
| <p>Note that between the time that your <em>StyledTextContent</em> | |
| implementation sends the <i>TextChangingEvent</i> and the <i>TextChangedEvent</i>, your | |
| implementation should not use API that will cause a scroll operation to occur or that will | |
| cause posted paints to be flushed. The <i>StyledText</i> widget also does not handle | |
| nested text content updates, so when processing text content changes, the application | |
| should not use API that will cause text updates to occur.</p> | |
| <p>The <i>StyledText</i> widget only modifies the cursor location when text | |
| is changed via user input. In all other instances (i.e., Case B and Case C in Figure 1 | |
| above), if the application wishes the cursor location to change as a result of the text | |
| content change, the following methods can be used to set the cursor location to an offset | |
| and to make that offset visible: </p> | |
| <tt> | |
| <pre> setSelection(offset, 0) | |
| showSelection()</pre> | |
| </tt> | |
| <h3>StyledTextContent Considerations</h3> | |
| <p>When implementing a <i>StyledTextContent</i>, the algorithm for updating | |
| the text content when modifications occur must be fast. If it is not, you will experience | |
| sluggishness during typing (i.e., as your <i>StyledTextContent</i> attempts to deal with | |
| the text change). Similarly, the algorithm for returning the text for a line should be | |
| fast. If it is not, you will experience sluggishness during scrolling. It is also | |
| important to ensure that your implementation scales well. When the text content gets | |
| larger, handling of text replace operations and answering lines should not be adversely | |
| affected from a performance standpoint. </p><p> | |
| For an example of a <i>StyledTextContent</i> implementation, refer to the class <i>org.eclipse.swt.custom.DefaultContent</i>. | |
| <i>DefaultContent</i> is the content implementation that the <i>StyledText</i> widget uses | |
| by default. </p> | |
| <h3>StyledTextContent Implementation Validation</h3> | |
| <p>The following Java™ class can be used to test a <i>StyledTextContent</i> | |
| implementation. When executed, the class will run through a number of test scenarios that | |
| exercise the <i>StyledTextContent</i> interface. To use the class, import it into the | |
| package that contains your <i>StyledTextContent</i> implementation. Then specify the fully | |
| qualified name of your implementation class as an argument to the<i> | |
| StyledTextContentSpec’s</i> <b>main</b> method. As each test scenario is | |
| executed, <b>System.out</b> will be updated to indicate whether or not each test | |
| passed. Note that this class is designed to work with a <em>StyledTextContent</em> | |
| implementation that supports "\r", "\n", and '\r\n" as line | |
| delimiters.</p> | |
| <p><a HREF="StyledTextContentSpec.java">StyledTextContentSpec.java</a> <br> | |
| </p> | |
| <h2>Implementing a LineStyleListener</h2> | |
| <h3>LineStyleListener Applicability</h3> | |
| <p>One of the main reasons to use a customized <i>LineStyleListener</i> is | |
| to support on-demand line styling. Instead of using the <b>replaceStyleRanges</b> (new in Eclipse 2.0), | |
| <b>setStyleRanges</b> and <b>setStyleRange</b> | |
| methods to statically set the style information for the widget’s text, | |
| style information can be provided dynamically on a line-by-line basis. </p> | |
| <p>If styles are determined based on the actual text content, on-demand line styling can | |
| be used since the text of each line is passed to the <i>LineStyleListener</i>. Text styles | |
| that are used for syntax highlighting can typically be provided dynamically. For example, | |
| except for block comments, all of Java’s syntactic elements occur on one line. It | |
| would be more efficient to use a dynamic, customized<i> LineStyleListener </i>vs. the <i>StyledText</i> | |
| API to implement Java syntax highlighting. If you used the <i>StyledText</i> API, style | |
| information would be maintained statically and any time text changes occurred this | |
| information would have to be updated (e.g., via the <b>setStyleRange</b> API). Using the <i>StyledText</i> | |
| API would also require you to parse the entire text up front. If on-demand line styling | |
| were used, the text would be parsed on an as needed basis, as it is displayed, thus | |
| improving initial load time. </p> | |
| <p>Another reason to use a customized <i>LineStyleListener</i> is to avoid duplicating | |
| data. When the <i>StyledText</i> methods <b>replaceStyleRanges</b>, <b>setStyleRanges</b> and <b>setStyleRange </b>are | |
| called, the <i>StyledText</i> widget will cache the <i>StyleRange</i> objects that are | |
| supplied as input parameters. However, if the information needed to construct style ranges | |
| is already stored by an object in your application, introducing a <i>LineStyleListener</i> | |
| that uses this object may be beneficial. </p> | |
| <p>You may also use a customized <i>LineStyleListener</i> in order to provide | |
| application-specific text coloring behavior. By default, when text changes occur in the <i>StyledText</i> | |
| widget, text colors are updated as follows: | |
| <ol> | |
| <ol> | |
| <li>If text is deleted, its associated style will also be deleted.</li> | |
| <li>If new text is inserted, it’s associated style will be null (i.e., | |
| the text background color will be the widget background color, the text foreground color | |
| will be the widget foreground color, and the text font style will be the widget font | |
| style).</li> | |
| </ol> | |
| </ol> | |
| <dir> | |
| <dir> | |
| <dir> | |
| <p>Note: A text replace operation is treated as a text delete operation | |
| followed by a text insert operation.</p> | |
| </dir> | |
| </dir> | |
| </dir> | |
| <p>The <i>StyledText</i> widget does not make any assumptions about how to | |
| apply styles when text is inserted or deleted because how to update the styles will depend | |
| on how the <i>StyledText</i> widget is being used. For example, if the widget is being | |
| used to support syntax highlighting for a source code editor, coloring will occur only | |
| when inserted text completes a keyword. Whereas, if the widget is being used to support | |
| text attributes for a word processor, inserted text will most likely take on the style | |
| values of the characters adjacent to the text. To provide specific text style updating | |
| behavior you can introduce your own <i>LineStyleListener</i>.</p> | |
| <h3>LineStyleListener Method Overview</h3> | |
| <p>The <i>LineStyleListener </i>interface defines the protocol that is used | |
| by the <i>StyledText</i> widget to get the styles of a displayed line.</p> | |
| <i> | |
| <p>void lineGetStyle (LineStyleEvent event) </i> | |
| <dir> | |
| <p>The <b>lineGetStyle</b> method is called when a line is about to be | |
| drawn in order to get the line’s style information. When the method is called, the <i>LineStyleEvent</i> | |
| has two input fields set - <b>LineStyleEvent.lineOffset</b> contains the start offset of | |
| the line and <b>LineStyleEvent<font COLOR="#000080">.</font>lineText</b> contains | |
| the text of the line. It is your responsibility to set the output field, <b>LineStyleEvent.styles</b>. | |
| This field defines the StyleRanges for the text line. The widget uses the StyleRanges for | |
| measuring and rendering text when the <b>lineGetStyle</b> listener method returns. Each <i>StyleRange</i> | |
| object describes the foreground color, background color and font style for a range of text | |
| in the particular line your listener was called for. The range start offset is relative to | |
| the beginning of the document, not the beginning of the line.</p> | |
| </dir> | |
| <h3>LineStyleListener Rules</h3> | |
| <p>If you implement your own <i>LineStyleListener </i>to supply line styles | |
| you may no longer use the <b>replaceStyleRanges</b>, <b>setStyleRange, setStyleRanges</b> or <b>getStyleRange</b> | |
| API. You can supply style information using a <i>LineStyleListener </i>or the API, but not | |
| both. </p><p> | |
| Ideally, you only have to use the line text and offset information supplied in the <i>LineStyleEvent</i> | |
| passed in to the <b>lineGetStyle</b> listener method to calculate line style information. | |
| However, it may be necessary for you to store style information and simply look up the | |
| styles when your listener method is called. In this case, you will have to update the | |
| style information when text changes occur. To do this, you can listen to the <b>extended | |
| modify event</b> (described in detail in our <a | |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a>). | |
| The <b>extended modify event</b> is sent after the widget text has been updated, but | |
| before the screen updates take effect. In this manner, inserted text can immediately use | |
| any styles that have been specified. Note that it does not make sense to use the <b>modify | |
| event</b> to update styles in this manner since the <b>modify event</b> does not contain | |
| any specific information about the text change (e.g., character offset of the inserted | |
| text). <a NAME="StyleRedraw"></a> </p> | |
| <p>If, as a result of the text change, style information before or after the lines | |
| modified by the text change is changed, you will have to redraw the affected area of the | |
| widget. For example, if you are using the <i>StyledText</i> widget for java syntax | |
| highlighting in a source code editor, deleting the beginning of a block comment (i.e., | |
| "/*") will affect lines after the line on which the text change occurred. In | |
| this instance, it would be the application’s responsibility to redraw the other | |
| comment lines. The <i>StyledText</i> widget will only update text lines based on the text | |
| change that occurred.</p> | |
| <p>The <i>StyledText</i> widget provides two redraw methods to support the above | |
| scenarios: </p> | |
| <tt> | |
| <pre> <font COLOR="#000080">public void</font> redrawRange(<font COLOR="#000080">int</font> start, <font | |
| COLOR="#000080">int</font> length, <font COLOR="#000080">boolean</font> clearBackground) | |
| <font | |
| COLOR="#000080">public void</font> redraw(<font COLOR="#000080">int</font> x, <font | |
| COLOR="#000080">int</font> y, <font COLOR="#000080">int</font> width, <font | |
| COLOR="#000080">int</font> height, <font COLOR="#000080">boolean</font> all)</pre> | |
| </tt> | |
| <p>The first, <b>redrawRange</b>, lets you specify the exact text range to | |
| redraw using character offsets. The second method, <b>redraw</b>, is the standard SWT | |
| redraw method that redraws a pixel based rectangle in the widget. SWT provides another | |
| redraw method, which is not shown here. It does not have any arguments and redraws the | |
| whole widget. You can use this method, but it will cause the text to flash since it | |
| performs a full clear and redraw of the widget.</p> | |
| <h3>LineStyleListener Considerations</h3> | |
| <p>If you implement your own <i>LineStyleListener</i>, keep in mind that | |
| the listener is called every time a line is rendered or scrolled and when a key is | |
| pressed. It is therefore important that the <b>lineGetStyle</b> method is fast and that it | |
| scales well for use with large amounts of text. If you experience a lag when typing and | |
| scrolling, your <i>LineStyleListener </i>implementation is too slow. If typing and | |
| scrolling is slow at the end of the text, your algorithm does not scale well. Note that | |
| unresponsive typing may also be caused by slow processing in <i>VerifyListeners</i>, <i>ModifyListeners</i> | |
| or <i>ExtendedModifyListeners </i>(see our <a | |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a> | |
| for more information about verify and modify listeners). </p> | |
| <p>If you implement a <i>LineStyleListener </i>and store the <i>StyleRange</i> objects | |
| (instead of calculating them on demand) it is advisable to merge adjacent styles that have | |
| the same style data. Merging StyleRanges improves the performance of text measuring and | |
| rendering in the <i>StyledText</i> widget because fewer <i>StyleRange</i> objects have to | |
| be processed. </p> | |
| <p><a NAME="LineStyleListenerRedraw"></a>If you redraw text during the <b>extended | |
| modified event</b>, make sure that you only redraw text ranges that actually have new | |
| styles. Unnecessary refresh operations can cause flash and degrade performance. See the | |
| discussion in the "Text Refresh" section of our <a | |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a> | |
| for more information about what flash means and what causes it. </p> | |
| <p>For an example of a <i>LineStyleListener</i> implementation, refer to the class <i>org.eclipse.swt.custom.DefaultLineStyler</i>. | |
| This class’ implementation of the <i>LineStyleListener</i> interface is the | |
| implementation that the <i>StyledText</i> widget uses by default. The class illustrates | |
| the concepts discussed above, including how to merge neighboring styles </p> | |
| <h3>LineStyleListener Example</h3> | |
| <p>The following example is the <b>lineGetStyle</b> method from a <i>LineStyleListener | |
| </i>that implements a simple Java syntax coloring strategy. The method constructs | |
| the <em>StyleRange</em> objects dynamically (i.e., every time they are requested) | |
| and merges similar styles for improved rendering performance. The example | |
| method code is taken from the SWT Java Syntax Viewer example, which is part | |
| of the example plugins that can be downloaded with the Eclipse SDK. The | |
| example code can be found in the plugins subdirectory<em> </em>org.eclipse.swt.examples. | |
| Three classes comprise the example - <em>JavaViewer</em>, <em>JavaLineStyler</em> | |
| and <em>JavaScanner</em>. The <em>JavaScanner</em> class is an inner | |
| class of <em>JavaLineStyler</em>.</p> | |
| <p>The <i>JavaScanner</i> class implements a Java syntax scanner. The <i>JavaScanner</i> | |
| public API includes the methods: </p> | |
| <tt> | |
| <pre> <font COLOR="#000080">void</font> setRange(String) | |
| <font COLOR="#000080">int</font> nextToken() | |
| <font | |
| COLOR="#000080">int</font> getStartOffset() | |
| <font COLOR="#000080">int</font> getLength()</pre> | |
| </tt> | |
| <p>as well as constants for the different Java syntax elements that need | |
| highlighting. In addition, the method <b>Color getColor(int)</b> returns a <i>Color</i> | |
| object for a given Java syntax constant. </p> | |
| <p>In the example, we set the text of the line we want to syntax scan in the Java scanner. | |
| We apply different colors to each token and keyword tokens are given a font style of bold. | |
| If the color for a token differs from the widget foreground color or if the token is a | |
| keyword (in which case a <i>StyleRange</i> would be necessary in order to specify a bold | |
| font style), we create a new <i>StyleRange</i> object for that element. If the previous <i>StyleRange</i> | |
| object has the same foreground color, background color and font style, it is considered | |
| similar and the two ranges can be merged. Merging StyleRanges improves the performance of | |
| text measuring and rendering in the <i>StyledText</i> widget.</p> | |
| <p>The lineGetStyle method also includes special processing for handling the <i>StyleRanges</i> | |
| that are created for keywords. In Java code, multiple keywords are often grouped together | |
| (e.g., "public static final int"). To minimize the number of <i>StyleRanges</i> | |
| that are created in this instance, spaces between the keywords are assigned a bold font | |
| style. This strategy will lead to only one <i>StyleRange</i> for a group of keywords vs. | |
| multiple <i>StyleRanges</i> (i.e., one for each keyword and one for each space between the | |
| keywords). The strategy will also minimize the number of font style changes that | |
| occur during rendering (i.e., the number of changes from the normal font to the bold | |
| font).</p> | |
| <p>When the complete line has been scanned and processed, the styles are copied into the <i>LineStyleEvent</i>. | |
| <dir> | |
| <dir> | |
| <tt><pre><font COLOR="#000080">public</font> <font COLOR="#000080">void</font> lineGetStyle(LineStyleEvent event) { | |
| Vector styles = <font | |
| COLOR="#000080">new</font> Vector(); | |
| <font COLOR="#800000"> // If the line is part of a block comment, create one style for the entire line. | |
| </font> <font | |
| COLOR="#000080">if</font> (inBlockComment(event.lineOffset, event.lineOffset + event.lineText.length())) { | |
| styles.addElement(new StyleRange(event.lineOffset, event.lineText.length(), getColor(COMMENT), <font | |
| COLOR="#000080">null</font>)); | |
| event.styles = <font COLOR="#000080">new</font> StyleRange[styles.size()]; | |
| styles.copyInto(event.styles); | |
| <font | |
| COLOR="#000080">return</font>; | |
| } | |
| <font COLOR="#000080"> int</font> token; | |
| StyleRange lastStyle; | |
| Color defaultFgColor = ((Control)event.widget).getForeground(); | |
| scanner.setRange(event.lineText); | |
| token = scanner.nextToken(); | |
| <font | |
| COLOR="#000080">while</font> (token != EOF) { | |
| <font COLOR="#000080">if</font> (token == OTHER) { | |
| <font | |
| COLOR="#800000"> // no syntax highlighting necessary | |
| </font> } <font | |
| COLOR="#000080">else</font> <font COLOR="#000080">if</font> ((token != WHITE) { | |
| <font | |
| COLOR="#800000"> // Only create a style if the token color is different than the widget's default foreground color and the | |
| // token’s fontStyle is not bold. | |
| </font> Color color = getColor(token); | |
| <font | |
| COLOR="#000080"> if</font> ((!color.equals(defaultFgColor)) || (token == KEY)) { | |
| StyleRange style = new StyleRange(scanner.getStartOffset() + event.lineOffset, scanner.getLength(), color, <font | |
| COLOR="#000080">null</font>); | |
| <font COLOR="#000080">if</font> (token == KEY) { | |
| style.fontStyle = SWT.BOLD; | |
| } | |
| <font | |
| COLOR="#000080">if</font> (styles.isEmpty()) { | |
| styles.addElement(style); | |
| } <font | |
| COLOR="#000080">else</font> { | |
| <font COLOR="#800000"> // Merge similar styles. | |
| </font> lastStyle = (StyleRange)styles.lastElement(); | |
| <font | |
| COLOR="#000080">if</font> (lastStyle.similarTo(style) && (lastStyle.start + lastStyle.length == style.start)) { | |
| lastStyle.length += style.length; | |
| } <font | |
| COLOR="#000080">else</font> { | |
| styles.addElement(style); | |
| } | |
| } | |
| } | |
| } <font | |
| COLOR="#000080">else if</font> (!styles.isEmpty()) && ((lastStyle=(StyleRange)styles.lastElement()).fontStyle == SWT.BOLD)) { | |
| <font | |
| COLOR="#800000"> // Have the white space take on the bold style before it to minimize the number of style ranges. | |
| </font> <font | |
| COLOR="#000080">int</font> start = scanner.getStartOffset() + event.lineOffset; | |
| <font | |
| COLOR="#800000"> </font><font COLOR="#000080">if</font> (lastStyle.start + lastStyle.length == start) { | |
| <font | |
| COLOR="#800000"> </font>lastStyle.length += scanner.getLength(); | |
| } | |
| } | |
| token= scanner.nextToken(); | |
| } | |
| event.styles = new StyleRange[styles.size()]; | |
| styles.copyInto(event.styles); | |
| } | |
| </pre> | |
| </tt></dir> | |
| </dir> | |
| <h2>Implementing a LineBackgroundListener</h2> | |
| <h3>LineBackgroundListener Applicability</h3> | |
| <p>As with the LineStyleListener, one reason to use a customized <i>LineBackgroundListener</i> | |
| is to avoid duplicating data. When the <i>StyledText</i> method <b>setLineBackground</b> | |
| is called, the <i>StyledText</i> widget will cache the colors for the specified lines. If | |
| this information is already stored by an object in your application, introducing a <i>LineBackgroundListener</i> | |
| that interacts with this object will eliminate data duplication. </p> | |
| <p>You may also use a customized <i>LineBackgroundListener</i> in order to provide | |
| application-specific line background coloring behavior. The <i>StyledText</i> widget | |
| maintains line background colors relative to text lines. By default, when text changes | |
| occur in the <i>StyledText</i> widget, line background colors are updated as follows: | |
| <ol> | |
| <ol> | |
| <li>If a line is deleted, its associated line background color will also be deleted.</li> | |
| <li>If a new line is inserted, its associated line background color will be null (i.e., the | |
| widget background color) and line background colors for existing lines of text are | |
| unchanged.</li> | |
| </ol> | |
| </ol> | |
| <p>For example, in the window below, a line background color of gray is used to emphasize | |
| the import statements. If text lines are inserted or deleted, you would want the line | |
| background color that is associated with the block of import statements to be maintained. </p> | |
| <p ALIGN="CENTER"><img SRC="Image8.gif" WIDTH="427" HEIGHT="169"></p> | |
| <p> | |
| <font SIZE="2"><br> | |
| </font>Your application may want line background colors to be updated | |
| differently when text changes occur. For example, if you want the background of every | |
| other line to be gray (e.g., in order to facilitate reading tabulated text), you would | |
| need to "shift" line background colors as lines are deleted or inserted. You | |
| could easily support this behavior by implementing your own <i>LineBackgroundListener</i>.</p> | |
| <h3>LineBackgroundListener Method Overview</h3> | |
| <p>The <i>LineBackgroundListener</i> interface defines the protocol that is | |
| used by the <i>StyledText</i> widget to get the line background colors to use when | |
| displaying the text. The interface, <i>LineBackgroundListener</i>, contains the methods | |
| that must be implemented in order to supply your own line background color implementation. | |
| </p> | |
| <i> | |
| <p>void lineGetBackground(LineBackgroundEvent event)</i> | |
| <dir> | |
| <p>This method is called when a line is to be drawn in order to get the line’s | |
| background color. <i>LineBackgroundEvent</i> will contain the start offset, <b>lineOffset</b>, | |
| and the text, <b>lineText</b>, of the line to be drawn. Fill in the background color in | |
| the <b>lineBackground</b> field. A value of null indicates that the default widget | |
| background should be used for the line. <br> | |
| </p> | |
| </dir> | |
| <p>In the following example, a LineBackgroundListener is used to color the background of | |
| every other line to light gray. The method assumes that we have a cache of colors indexed | |
| by RGB value. | |
| <dir> | |
| <dir> | |
| <tt><pre><font COLOR="#000080">public void</font> lineGetBackground(LineBackgroundEvent event) { | |
| <font | |
| COLOR="#000080">int</font> line = ((StyledText) event.widget).getLineAtOffset(event.lineOffset); | |
| <font | |
| COLOR="#000080">if</font> ((line % 2) == 1) { | |
| event.lineBackground = (Color)colors.get(<font | |
| COLOR="#000080">new</font> RGB(222, 222, 222)); | |
| } | |
| }</pre> | |
| </tt></dir> | |
| </dir> | |
| <h3>LineBackgroundListener Rules</h3> | |
| <p>Line background colors are drawn for the width of the widget, not the | |
| width of the text. If a text background color is defined (i.e., via a <i>StyleRange</i>), | |
| this color will overlay the line background color. </p> | |
| <p>If you implement your own <i>LineBackgroundListener, </i>the following <i>StyledText</i> | |
| methods are not applicable and should not be used since your <i>LineBackgroundListener</i> | |
| will be maintaining and storing line background colors: | |
| <dir> | |
| <dir> | |
| <p><tt><font COLOR="#000080">public</font> Color getLineBackground(<font COLOR="#000080">int</font> | |
| index)</tt><font SIZE="2"> <br> | |
| </font><tt><font COLOR="#000080">public</font> <font COLOR="#000080">void</font> | |
| setLineBackground(<font COLOR="#000080">int</font> startLine, <font COLOR="#000080">int</font> | |
| lineCount, Color background)</tt> </p> | |
| </dir> | |
| </dir> | |
| <h3>LineBackgroundListener Considerations</h3> | |
| <p>When you implement a <i>LineBackgroundListener</i>, since the <b>setLineBackground</b> | |
| API is no longer available, you will be responsible for refreshing line background colors | |
| within the widget. For example, if a text content change alters line background colors for | |
| lines other than the lines on which the text change occurred, it would be the | |
| LineBackgroundListener’s responsibility to redraw these lines. You can refresh line | |
| background colors by using the <b>redraw(int x, int y, int width, int</b><b>height, boolean all)</b> method. The input parameters | |
| to this method specify a pixel-based rectangle. You can use other <i>StyledText</i> API | |
| (e.g., <b>getLocationAtOffset</b>) to calculate the rectangle for a line. </p><p> | |
| <a NAME="LineBackgroundListenerRedraw"></a>When you implement a <i>LineBackgroundListener</i> | |
| you should make sure that you only initiate line background updates when necessary. | |
| Unnecessary refreshing of lines will cause flash and degrade performance of the <i>StyledText</i> | |
| widget, as discussed in the Text Refresh section of our <a | |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a>.</p><p> | |
| For an example of how to implement a <i>LineBackgroundListener</i>, refer to the class <i>org.eclipse.swt.custom.DefaultLineStyler</i>. | |
| The <i>DefaultLineStyler </i>implements the<i> LineBackgroundListener</i> behavior that | |
| the <i>StyledText</i> widget uses by default. | |
| <dl> | |
| <dt> </dt> | |
| </dl> | |
| <p><small>Java and all Java-based trademarks and logos are trademarks or registered | |
| trademarks of Sun Microsystems, Inc. in the United States, other countries, or | |
| both.</small></p> | |
| </body> | |
| </html> |