Java Tutorial/SWT/StyledText
Содержание
- 1 A simple editor
- 2 Create a StyledText that scrolls vertically, wraps text, and displays a border:
- 3 Creating a StyledText Widget
- 4 Draw a box around text
- 5 Getting Statistics: Caret Offset, Total Lines of Text, Total Characters and Current Line
- 6 Limit the number of characters that the StyledText accepts
- 7 Make a StyledText read-only
- 8 Print StyledText out
- 9 Print to the default printer in a separate thread
- 10 Replace Text Range
- 11 Set page format for printing
- 12 StyledText: embed controls
- 13 StyledText: embed images
- 14 StyledText: use gradient background
- 15 Understanding the Repercussions
- 16 Using the Clipboard
A simple editor
<source lang="java">
//Code revised from /* The Definitive Guide to SWT and JFace by Robert Harris and Rob Warner Apress 2004
- /
import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Stack; import java.util.StringTokenizer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ExtendedModifyEvent; import org.eclipse.swt.custom.ExtendedModifyListener; import org.eclipse.swt.custom.LineStyleEvent; import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.custom.ST; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.StyledTextPrintOptions; import org.eclipse.swt.events.ArmEvent; import org.eclipse.swt.events.ArmListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; public class PmpEditor {
// The number of operations that can be undone private static final int UNDO_LIMIT = 500; // Contains a reference to this application private static PmpEditor app; // Contains a reference to the main window private Shell shell; // Displays the file private StyledText st; // The full path of the current file private String filename; // The font for the StyledText private Font font; // The label to display statistics private Label status; // The print options and printer private StyledTextPrintOptions options; private Printer printer; // The stack used to store the undo information private Stack changes; // Flag to set before performaing an undo, so the undo // operation doesn"t get stored with the rest of the undo // information private boolean ignoreUndo = false; // Syntax data for the current extension private SyntaxData sd; // Line style listener private PmpeLineStyleListener lineStyleListener; /** * Gets the reference to this application * * @return HexEditor */ public static PmpEditor getApp() { return app; } /** * Constructs a PmpEditor */ public PmpEditor() { app = this; changes = new Stack(); // Set up the printing options options = new StyledTextPrintOptions(); options.footer = StyledTextPrintOptions.SEPARATOR + StyledTextPrintOptions.PAGE_TAG + StyledTextPrintOptions.SEPARATOR + "Confidential"; } /** * Runs the application */ public void run() { Display display = new Display(); shell = new Shell(display); // Choose a monospaced font font = new Font(display, "Terminal", 12, SWT.NONE); createContents(shell); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } font.dispose(); display.dispose(); if (printer != null) printer.dispose(); } /** * Creates the main window"s contents * * @param shell * the main window */ private void createContents(Shell shell) { // Set the layout and the menu bar shell.setLayout(new FormLayout()); shell.setMenuBar(new PmpEditorMenu(shell).getMenu()); // Create the status bar status = new Label(shell, SWT.BORDER); FormData data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.bottom = new FormAttachment(100, 0); data.height = status.ruputeSize(SWT.DEFAULT, SWT.DEFAULT).y; status.setLayoutData(data); // Create the styled text st = new StyledText(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); data = new FormData(); data.left = new FormAttachment(0); data.right = new FormAttachment(100); data.top = new FormAttachment(0); data.bottom = new FormAttachment(status); st.setLayoutData(data); // Set the font st.setFont(font); // Add Brief delete next word // Use SWT.MOD1 instead of SWT.CTRL for portability st.setKeyBinding("k" | SWT.MOD1, ST.DELETE_NEXT); // Add vi end of line (kind of) // Use SWT.MOD1 instead of SWT.CTRL for portability // Use SWT.MOD2 instead of SWT.SHIFT for portability // Shift+4 is $ st.setKeyBinding("4" | SWT.MOD1 | SWT.MOD2, ST.LINE_END); // Handle key presses st.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent event) { // Update the status bar updateStatus(); } }); // Handle text modifications st.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent event) { // Update the status bar updateStatus(); // Update the comments if (lineStyleListener != null) { lineStyleListener.refreshMultilineComments(st.getText()); st.redraw(); } } }); // Store undo information st.addExtendedModifyListener(new ExtendedModifyListener() { public void modifyText(ExtendedModifyEvent event) { if (!ignoreUndo) { // Push this change onto the changes stack changes.push(new TextChange(event.start, event.length, event.replacedText)); if (changes.size() > UNDO_LIMIT) changes.remove(0); } } }); // Update the title bar and the status bar updateTitle(); updateStatus(); } /** * Opens a file */ public void openFile() { FileDialog dlg = new FileDialog(shell); String temp = dlg.open(); if (temp != null) { try { // Get the file"s contents String text = PmpeIoManager.getFile(temp); // File loaded, so save the file name filename = temp; // Update the syntax properties to use updateSyntaxData(); // Put the new file"s data in the StyledText st.setText(text); // Update the title bar updateTitle(); // Delete any undo information changes.clear(); } catch (IOException e) { showError(e.getMessage()); } } } /** * Saves a file */ public void saveFile() { if (filename == null) { saveFileAs(); } else { try { // Save the file and update the title bar based on the new file name PmpeIoManager.saveFile(filename, st.getText().getBytes()); updateTitle(); } catch (IOException e) { showError(e.getMessage()); } } } /** * Saves a file under a different name */ public void saveFileAs() { FileDialog dlg = new FileDialog(shell); if (filename != null) { dlg.setFileName(filename); } String temp = dlg.open(); if (temp != null) { filename = temp; // The extension may have changed; update the syntax data accordingly updateSyntaxData(); saveFile(); } } /** * Prints the document to the default printer */ public void print() { if (printer == null) printer = new Printer(); options.header = StyledTextPrintOptions.SEPARATOR + filename + StyledTextPrintOptions.SEPARATOR; st.print(printer, options).run(); } /** * Cuts the current selection to the clipboard */ public void cut() { st.cut(); } /** * Copies the current selection to the clipboard */ public void copy() { st.copy(); } /** * Pastes the clipboard"s contents */ public void paste() { st.paste(); } /** * Selects all the text */ public void selectAll() { st.selectAll(); } /** * Undoes the last change */ public void undo() { // Make sure undo stack isn"t empty if (!changes.empty()) { // Get the last change TextChange change = (TextChange) changes.pop(); // Set the flag. Otherwise, the replaceTextRange call will get placed // on the undo stack ignoreUndo = true; // Replace the changed text st.replaceTextRange(change.getStart(), change.getLength(), change.getReplacedText()); // Move the caret st.setCaretOffset(change.getStart()); // Scroll the screen st.setTopIndex(st.getLineAtOffset(change.getStart())); ignoreUndo = false; } } /** * Toggles word wrap */ public void toggleWordWrap() { st.setWordWrap(!st.getWordWrap()); } /** * Gets the current word wrap settings * * @return boolean */ public boolean getWordWrap() { return st.getWordWrap(); } /** * Shows an about box */ public void about() { MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK); mb.setMessage("Poor Man"s Programming Editor"); mb.open(); } /** * Updates the title bar */ private void updateTitle() { String fn = filename == null ? "Untitled" : filename; shell.setText(fn + " -- PmPe"); } /** * Updates the status bar */ private void updateStatus() { // Show the offset into the file, the total number of characters in the // file, // the current line number (1-based) and the total number of lines StringBuffer buf = new StringBuffer(); buf.append("Offset: "); buf.append(st.getCaretOffset()); buf.append("\tChars: "); buf.append(st.getCharCount()); buf.append("\tLine: "); buf.append(st.getLineAtOffset(st.getCaretOffset()) + 1); buf.append(" of "); buf.append(st.getLineCount()); status.setText(buf.toString()); } /** * Updates the syntax data based on the filename"s extension */ private void updateSyntaxData() { // Determine the extension of the current file String extension = ""; if (filename != null) { int pos = filename.lastIndexOf("."); if (pos > -1 && pos < filename.length() - 2) { extension = filename.substring(pos + 1); } } // Get the syntax data for the extension sd = SyntaxManager.getSyntaxData(extension); // Reset the line style listener if (lineStyleListener != null) { st.removeLineStyleListener(lineStyleListener); } lineStyleListener = new PmpeLineStyleListener(sd); st.addLineStyleListener(lineStyleListener); // Redraw the contents to reflect the new syntax data st.redraw(); } /** * Shows an error message * * @param error * the text to show */ private void showError(String error) { MessageBox mb = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK); mb.setMessage(error); mb.open(); } /** * The application entry point * * @param args * the command line arguments */ public static void main(String[] args) { new PmpEditor().run(); }
} class PmpEditorMenu {
// The underlying menu this class wraps Menu menu = null; /** * Constructs a PmpEditorMenu * * @param shell * the parent shell */ public PmpEditorMenu(final Shell shell) { // Create the menu menu = new Menu(shell, SWT.BAR); // Create the File top-level menu MenuItem item = new MenuItem(menu, SWT.CASCADE); item.setText("File"); Menu dropMenu = new Menu(shell, SWT.DROP_DOWN); item.setMenu(dropMenu); // Create File->Open item = new MenuItem(dropMenu, SWT.NULL); item.setText("Open...\tCtrl+O"); item.setAccelerator(SWT.CTRL + "O"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().openFile(); } }); // Create File->Save item = new MenuItem(dropMenu, SWT.NULL); item.setText("Save\tCtrl+S"); item.setAccelerator(SWT.CTRL + "S"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().saveFile(); } }); // Create File->Save As item = new MenuItem(dropMenu, SWT.NULL); item.setText("Save As..."); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().saveFileAs(); } }); new MenuItem(dropMenu, SWT.SEPARATOR); // Create File->Print item = new MenuItem(dropMenu, SWT.NULL); item.setText("Print\tCtrl+P"); item.setAccelerator(SWT.CTRL + "P"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().print(); } }); new MenuItem(dropMenu, SWT.SEPARATOR); // Create File->Exit item = new MenuItem(dropMenu, SWT.NULL); item.setText("Exit\tAlt+F4"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { shell.close(); } }); // Create Edit item = new MenuItem(menu, SWT.CASCADE); item.setText("Edit"); dropMenu = new Menu(shell, SWT.DROP_DOWN); item.setMenu(dropMenu); // Create Edit->Cut item = new MenuItem(dropMenu, SWT.NULL); item.setText("Cut\tCtrl+X"); item.setAccelerator(SWT.CTRL + "X"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().cut(); } }); // Create Edit->Copy item = new MenuItem(dropMenu, SWT.NULL); item.setText("Copy\tCtrl+C"); item.setAccelerator(SWT.CTRL + "C"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().copy(); } }); // Create Edit->Paste item = new MenuItem(dropMenu, SWT.NULL); item.setText("Paste\tCtrl+V"); item.setAccelerator(SWT.CTRL + "V"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().paste(); } }); new MenuItem(dropMenu, SWT.SEPARATOR); // Create Select All item = new MenuItem(dropMenu, SWT.NULL); item.setText("Select All\tCtrl+A"); item.setAccelerator(SWT.CTRL + "A"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().selectAll(); } }); new MenuItem(dropMenu, SWT.SEPARATOR); // Create Undo item = new MenuItem(dropMenu, SWT.NULL); item.setText("Undo\tCtrl+Z"); item.setAccelerator(SWT.CTRL + "Z"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().undo(); } }); new MenuItem(dropMenu, SWT.SEPARATOR); // Create Word Wrap final MenuItem wwItem = new MenuItem(dropMenu, SWT.CHECK); wwItem.setText("Word Wrap\tCtrl+W"); wwItem.setAccelerator(SWT.CTRL + "W"); wwItem.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().toggleWordWrap(); } }); wwItem.addArmListener(new ArmListener() { public void widgetArmed(ArmEvent event) { wwItem.setSelection(PmpEditor.getApp().getWordWrap()); } }); // Create Help item = new MenuItem(menu, SWT.CASCADE); item.setText("Help"); dropMenu = new Menu(shell, SWT.DROP_DOWN); item.setMenu(dropMenu); // Create Help->About item = new MenuItem(dropMenu, SWT.NULL); item.setText("About\tCtrl+A"); item.setAccelerator(SWT.CTRL + "A"); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { PmpEditor.getApp().about(); } }); } /** * Gets the underlying menu * * @return Menu */ public Menu getMenu() { return menu; }
} class PmpeIoManager {
/** * Gets a file (loads it) from the filesystem * * @param filename * the full path of the file * @return String * @throws IOException * if file cannot be loaded */ public static String getFile(String filename) throws IOException { InputStream in = new BufferedInputStream(new FileInputStream(filename)); StringBuffer buf = new StringBuffer(); int c; while ((c = in.read()) != -1) { buf.append((char) c); } return buf.toString(); } /** * Saves a file * * @param filename * the full path of the file to save * @param data * the data to save * @throws IOException * if file cannot be saved */ public static void saveFile(String filename, byte[] data) throws IOException { File outputFile = new File(filename); FileOutputStream out = new FileOutputStream(outputFile); out.write(data); out.close(); }
} class TextChange {
// The starting offset of the change private int start; // The length of the change private int length; // The replaced text String replacedText; /** * Constructs a TextChange * * @param start * the starting offset of the change * @param length * the length of the change * @param replacedText * the text that was replaced */ public TextChange(int start, int length, String replacedText) { this.start = start; this.length = length; this.replacedText = replacedText; } /** * Returns the start * * @return int */ public int getStart() { return start; } /** * Returns the length * * @return int */ public int getLength() { return length; } /** * Returns the replacedText * * @return String */ public String getReplacedText() { return replacedText; }
} /**
* This class contains information for syntax coloring and styling for an * extension */
class SyntaxData {
private String extension; private Collection keywords; private String punctuation; private String comment; private String multiLineCommentStart; private String multiLineCommentEnd; /** * Constructs a SyntaxData * * @param extension * the extension */ public SyntaxData(String extension) { this.extension = extension; } /** * Gets the extension * * @return String */ public String getExtension() { return extension; } /** * Gets the comment * * @return String */ public String getComment() { return comment; } /** * Sets the comment * * @param comment * The comment to set. */ public void setComment(String comment) { this.rument = comment; } /** * Gets the keywords * * @return Collection */ public Collection getKeywords() { return keywords; } /** * Sets the keywords * * @param keywords * The keywords to set. */ public void setKeywords(Collection keywords) { this.keywords = keywords; } /** * Gets the multiline comment end * * @return String */ public String getMultiLineCommentEnd() { return multiLineCommentEnd; } /** * Sets the multiline comment end * * @param multiLineCommentEnd * The multiLineCommentEnd to set. */ public void setMultiLineCommentEnd(String multiLineCommentEnd) { this.multiLineCommentEnd = multiLineCommentEnd; } /** * Gets the multiline comment start * * @return String */ public String getMultiLineCommentStart() { return multiLineCommentStart; } /** * Sets the multiline comment start * * @param multiLineCommentStart * The multiLineCommentStart to set. */ public void setMultiLineCommentStart(String multiLineCommentStart) { this.multiLineCommentStart = multiLineCommentStart; } /** * Gets the punctuation * * @return String */ public String getPunctuation() { return punctuation; } /** * Sets the punctuation * * @param punctuation * The punctuation to set. */ public void setPunctuation(String punctuation) { this.punctuation = punctuation; }
} /**
* This class manages the syntax coloring and styling data */
class SyntaxManager {
// Lazy cache of SyntaxData objects private static Map data = new Hashtable(); /** * Gets the syntax data for an extension */ public static synchronized SyntaxData getSyntaxData(String extension) { // Check in cache SyntaxData sd = (SyntaxData) data.get(extension); if (sd == null) { // Not in cache; load it and put in cache sd = loadSyntaxData(extension); if (sd != null) data.put(sd.getExtension(), sd); } return sd; } /** * Loads the syntax data for an extension * * @param extension * the extension to load * @return SyntaxData */ private static SyntaxData loadSyntaxData(String extension) { SyntaxData sd = null; try { ResourceBundle rb = ResourceBundle.getBundle("examples.ch11." + extension); sd = new SyntaxData(extension); sd.setComment(rb.getString("comment")); sd.setMultiLineCommentStart(rb.getString("multilinecommentstart")); sd.setMultiLineCommentEnd(rb.getString("multilinecommentend")); // Load the keywords Collection keywords = new ArrayList(); for (StringTokenizer st = new StringTokenizer(rb.getString("keywords"), " "); st .hasMoreTokens();) { keywords.add(st.nextToken()); } sd.setKeywords(keywords); // Load the punctuation sd.setPunctuation(rb.getString("punctuation")); } catch (MissingResourceException e) { // Ignore } return sd; }
} /**
* This class performs the syntax highlighting and styling for Pmpe */
class PmpeLineStyleListener implements LineStyleListener {
// Colors private static final Color COMMENT_COLOR = Display.getCurrent().getSystemColor( SWT.COLOR_DARK_GREEN); private static final Color COMMENT_BACKGROUND = Display.getCurrent().getSystemColor( SWT.COLOR_GRAY); private static final Color PUNCTUATION_COLOR = Display.getCurrent().getSystemColor( SWT.COLOR_DARK_CYAN); private static final Color KEYWORD_COLOR = Display.getCurrent().getSystemColor( SWT.COLOR_DARK_MAGENTA); // Holds the syntax data private SyntaxData syntaxData; // Holds the offsets for all multiline comments List commentOffsets; /** * PmpeLineStyleListener constructor * * @param syntaxData * the syntax data to use */ public PmpeLineStyleListener(SyntaxData syntaxData) { this.syntaxData = syntaxData; commentOffsets = new LinkedList(); } /** * Refreshes the offsets for all multiline comments in the parent StyledText. * The parent StyledText should call this whenever its text is modified. Note * that this code doesn"t ignore comment markers inside strings. * * @param text * the text from the StyledText */ public void refreshMultilineComments(String text) { // Clear any stored offsets commentOffsets.clear(); if (syntaxData != null) { // Go through all the instances of COMMENT_START for (int pos = text.indexOf(syntaxData.getMultiLineCommentStart()); pos > -1; pos = text .indexOf(syntaxData.getMultiLineCommentStart(), pos)) { // offsets[0] holds the COMMENT_START offset // and COMMENT_END holds the ending offset int[] offsets = new int[2]; offsets[0] = pos; // Find the corresponding end comment. pos = text.indexOf(syntaxData.getMultiLineCommentEnd(), pos); // If no corresponding end comment, use the end of the text offsets[1] = pos == -1 ? text.length() - 1 : pos + syntaxData.getMultiLineCommentEnd().length() - 1; pos = offsets[1]; // Add the offsets to the collection commentOffsets.add(offsets); } } } /** * Checks to see if the specified section of text begins inside a multiline * comment. Returns the index of the closing comment, or the end of the line * if the whole line is inside the comment. Returns -1 if the line doesn"t * begin inside a comment. * * @param start * the starting offset of the text * @param length * the length of the text * @return int */ private int getBeginsInsideComment(int start, int length) { // Assume section doesn"t being inside a comment int index = -1; // Go through the multiline comment ranges for (int i = 0, n = commentOffsets.size(); i < n; i++) { int[] offsets = (int[]) commentOffsets.get(i); // If starting offset is past range, quit if (offsets[0] > start + length) break; // Check to see if section begins inside a comment if (offsets[0] <= start && offsets[1] >= start) { // It does; determine if the closing comment marker is inside // this section index = offsets[1] > start + length ? start + length : offsets[1] + syntaxData.getMultiLineCommentEnd().length() - 1; } } return index; } /** * Called by StyledText to get styles for a line */ public void lineGetStyle(LineStyleEvent event) { // Only do styles if syntax data has been loaded if (syntaxData != null) { // Create collection to hold the StyleRanges List styles = new ArrayList(); int start = 0; int length = event.lineText.length(); // Check if line begins inside a multiline comment int mlIndex = getBeginsInsideComment(event.lineOffset, event.lineText.length()); if (mlIndex > -1) { // Line begins inside multiline comment; create the range styles.add(new StyleRange(event.lineOffset, mlIndex - event.lineOffset, COMMENT_COLOR, COMMENT_BACKGROUND)); start = mlIndex; } // Do punctuation, single-line comments, and keywords while (start < length) { // Check for multiline comments that begin inside this line if (event.lineText.indexOf(syntaxData.getMultiLineCommentStart(), start) == start) { // Determine where comment ends int endComment = event.lineText.indexOf(syntaxData.getMultiLineCommentEnd(), start); // If comment doesn"t end on this line, extend range to end of line if (endComment == -1) endComment = length; else endComment += syntaxData.getMultiLineCommentEnd().length(); styles.add(new StyleRange(event.lineOffset + start, endComment - start, COMMENT_COLOR, COMMENT_BACKGROUND)); // Move marker start = endComment; } // Check for single line comments else if (event.lineText.indexOf(syntaxData.getComment(), start) == start) { // Comment rest of line styles.add(new StyleRange(event.lineOffset + start, length - start, COMMENT_COLOR, COMMENT_BACKGROUND)); // Move marker start = length; } // Check for punctuation else if (syntaxData.getPunctuation().indexOf(event.lineText.charAt(start)) > -1) { // Add range for punctuation styles.add(new StyleRange(event.lineOffset + start, 1, PUNCTUATION_COLOR, null)); ++start; } else if (Character.isLetter(event.lineText.charAt(start))) { // Get the next word StringBuffer buf = new StringBuffer(); int i = start; // Call any consecutive letters a word for (; i < length && Character.isLetter(event.lineText.charAt(i)); i++) { buf.append(event.lineText.charAt(i)); } // See if the word is a keyword if (syntaxData.getKeywords().contains(buf.toString())) { // It"s a keyword; create the StyleRange styles.add(new StyleRange(event.lineOffset + start, i - start, KEYWORD_COLOR, null, SWT.BOLD)); } // Move the marker to the last char (the one that wasn"t a letter) // so it can be retested in the next iteration through the loop start = i; } else // It"s nothing we"re interested in; advance the marker ++start; } // Copy the StyleRanges back into the event event.styles = (StyleRange[]) styles.toArray(new StyleRange[0]); } }
}</source>
Create a StyledText that scrolls vertically, wraps text, and displays a border:
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextCreate {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText text = new StyledText(shell, SWT.V_SCROLL | SWT.WRAP | SWT.BORDER); text.setBounds(10,10,100,100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Creating a StyledText Widget
<source lang="java">
StyledText(Composite parent, int style)</source>
StyledText Styles
ConstantDescriptionSWT.BORDERDraws a border around the StyledText.SWT.SINGLECreates a single-line StyledText.SWT.MULTICreates a multiline StyledText. This is the default.SWT.H_SCROLLEnables horizontal scrolling.SWT.V_SCROLLEnables vertical scrolling.SWT.WRAPTurns on word wrapping, trumping the horizontal scrolling style.SWT.READ_ONLYMakes the StyledText read-only.SWT.FULL_SELECTIONCauses redrawing operations to redraw the full line instead of only the invalidated portion.
Draw a box around text
<source lang="java">
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
//package org.eclipse.swt.snippets; /*
* StyledText snippet: Draw a box around text. * * For a list of all SWT example snippets see * http://www.eclipse.org/swt/snippets/ */
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; public class TextAroundBox {
static String SEARCH_STRING = "box"; public static void main(String[] args) { final Display display = new Display(); final Color RED = display.getSystemColor(SWT.COLOR_RED); Shell shell = new Shell(display); shell.setBounds(10, 10, 250, 250); final StyledText text = new StyledText(shell, SWT.NONE); text.setBounds(10, 10, 200, 200); text.addListener(SWT.Paint, new Listener() { public void handleEvent(Event event) { String contents = text.getText(); int stringWidth = event.gc.stringExtent(SEARCH_STRING).x; int lineHeight = text.getLineHeight(); event.gc.setForeground(RED); int index = contents.indexOf(SEARCH_STRING); while (index != -1) { Point topLeft = text.getLocationAtOffset(index); event.gc.drawRectangle(topLeft.x - 1, topLeft.y, stringWidth + 1, lineHeight - 1); index = contents.indexOf(SEARCH_STRING, index + 1); } } }); text .setText("This demonstrates drawing a box\naround every occurrence of the word\nbox in the StyledText"); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); }
}</source>
Getting Statistics: Caret Offset, Total Lines of Text, Total Characters and Current Line
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextStatistics {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.setText("12345"); System.out.println("Caret Offset: " + styledText.getCaretOffset()); System.out.println("Total Lines of Text: " + styledText.getLineCount()); System.out.println("Total Characters: " + styledText.getCharCount()); System.out.println("Current Line: " + (styledText.getLineAtOffset(styledText.getCaretOffset()) + 1)); styledText.setBounds(10,10,100,100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Limit the number of characters that the StyledText accepts
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextLimit {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.setText("12345"); styledText.setTextLimit(10); styledText.setBounds(10, 10, 100, 100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Make a StyledText read-only
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextReadOnly {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.setText("12345"); styledText.setEditable(false); styledText.setBounds(10, 10, 100, 100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Print StyledText out
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextPrint {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.setText("12345"); styledText.print(); styledText.setBounds(10,10,100,100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Print to the default printer in a separate thread
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextPrintSeparateThread {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.print(new Printer()).run(); styledText.setBounds(10,10,100,100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Replace Text Range
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextReplaceTextRange {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); final StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.setText("12345");
styledText.replaceTextRange(2, 3, "A"); styledText.setBounds(10, 10, 100, 100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
Set page format for printing
- print the name of the file on top of each page,
- print the page number at the bottom of each page,
- print the word "Confidential" in the lower-right corner,
- print the text in the appropriate colors and styles
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.StyledTextPrintOptions; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextPrintFormat {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText styledText = new StyledText(shell, SWT.V_SCROLL | SWT.BORDER); styledText.setText("12345"); StyledTextPrintOptions options = new StyledTextPrintOptions(); options.header = StyledTextPrintOptions.SEPARATOR + "myfile.txt" + StyledTextPrintOptions.SEPARATOR; options.footer = StyledTextPrintOptions.SEPARATOR + StyledTextPrintOptions.PAGE_TAG + StyledTextPrintOptions.SEPARATOR + "Confidential"; options.printLineBackground = true; options.printTextBackground = true; options.printTextFontStyle = true; options.printTextForeground = true; styledText.print(new Printer(), options).run(); styledText.setBounds(10, 10, 100, 100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>
StyledText: embed controls
<source lang="java">
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
//package org.eclipse.swt.snippets; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.PaintObjectEvent; import org.eclipse.swt.custom.PaintObjectListener; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GlyphMetrics; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.rubo; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /**
* StyledText snippet: embed controls * * For a list of all SWT example snippets see * http://www.eclipse.org/swt/snippets/ * * @since 3.2 */
public class StyledTextEmbebControls {
static StyledText styledText; static String text = "This snippet shows how to embed widgets in a StyledText.\n" + "Here is one: \uFFFC, and here is another: \uFFFC."; static int[] offsets; static Control[] controls; static int MARGIN = 5; static void addControl(Control control, int offset) { StyleRange style = new StyleRange(); style.start = offset; style.length = 1; control.pack(); Rectangle rect = control.getBounds(); int ascent = 2 * rect.height / 3; int descent = rect.height - ascent; style.metrics = new GlyphMetrics(ascent + MARGIN, descent + MARGIN, rect.width + 2 * MARGIN); styledText.setStyleRange(style); } public static void main(String[] args) { final Display display = new Display(); Font font = new Font(display, "Tahoma", 32, SWT.NORMAL); final Shell shell = new Shell(display); shell.setLayout(new GridLayout()); styledText = new StyledText(shell, SWT.WRAP | SWT.BORDER); styledText.setFont(font); styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); styledText.setText(text); controls = new Control[2]; Button button = new Button(styledText, SWT.PUSH); button.setText("Button 1"); controls[0] = button; Combo combo = new Combo(styledText, SWT.NONE); combo.add("item 1"); combo.add("another item"); controls[1] = combo; offsets = new int[controls.length]; int lastOffset = 0; for (int i = 0; i < controls.length; i++) { int offset = text.indexOf("\uFFFC", lastOffset); offsets[i] = offset; addControl(controls[i], offsets[i]); lastOffset = offset + 1; } // use a verify listener to keep the offsets up to date styledText.addVerifyListener(new VerifyListener() { public void verifyText(VerifyEvent e) { int start = e.start; int replaceCharCount = e.end - e.start; int newCharCount = e.text.length(); for (int i = 0; i < offsets.length; i++) { int offset = offsets[i]; if (start <= offset && offset < start + replaceCharCount) { // this widget is being deleted from the text if (controls[i] != null && !controls[i].isDisposed()) { controls[i].dispose(); controls[i] = null; } offset = -1; } if (offset != -1 && offset >= start) offset += newCharCount - replaceCharCount; offsets[i] = offset; } } }); // reposition widgets on paint event styledText.addPaintObjectListener(new PaintObjectListener() { public void paintObject(PaintObjectEvent event) { StyleRange style = event.style; int start = style.start; for (int i = 0; i < offsets.length; i++) { int offset = offsets[i]; if (start == offset) { Point pt = controls[i].getSize(); int x = event.x + MARGIN; int y = event.y + event.ascent - 2 * pt.y / 3; controls[i].setLocation(x, y); break; } } } }); shell.setSize(400, 400); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } font.dispose(); display.dispose(); }
}</source>
StyledText: embed images
<source lang="java">
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
//package org.eclipse.swt.snippets; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.PaintObjectEvent; import org.eclipse.swt.custom.PaintObjectListener; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GlyphMetrics; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; /**
* StyledText snippet: embed images * * For a list of all SWT example snippets see * http://www.eclipse.org/swt/snippets/ * * @since 3.2 */
public class StyledTextEmbedImages {
static StyledText styledText; static String text = "This snippet shows how to embed images in a StyledText.\n" + "Here is one: \uFFFC, and here is another: \uFFFC." + "Use the add button to add an image from your filesystem to the StyledText at the current caret offset."; static Image[] images; static int[] offsets; static void addImage(Image image, int offset) { StyleRange style = new StyleRange(); style.start = offset; style.length = 1; Rectangle rect = image.getBounds(); style.metrics = new GlyphMetrics(rect.height, 0, rect.width); styledText.setStyleRange(style); } public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); shell.setLayout(new GridLayout()); styledText = new StyledText(shell, SWT.WRAP | SWT.BORDER); styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); styledText.setText(text); images = new Image[] { display.getSystemImage(SWT.ICON_QUESTION), display.getSystemImage(SWT.ICON_INFORMATION), }; offsets = new int[images.length]; int lastOffset = 0; for (int i = 0; i < images.length; i++) { int offset = text.indexOf("\uFFFC", lastOffset); offsets[i] = offset; addImage(images[i], offset); lastOffset = offset + 1; } // use a verify listener to keep the offsets up to date styledText.addVerifyListener(new VerifyListener() { public void verifyText(VerifyEvent e) { int start = e.start; int replaceCharCount = e.end - e.start; int newCharCount = e.text.length(); for (int i = 0; i < offsets.length; i++) { int offset = offsets[i]; if (start <= offset && offset < start + replaceCharCount) { // this image is being deleted from the text if (images[i] != null && !images[i].isDisposed()) { images[i].dispose(); images[i] = null; } offset = -1; } if (offset != -1 && offset >= start) offset += newCharCount - replaceCharCount; offsets[i] = offset; } } }); styledText.addPaintObjectListener(new PaintObjectListener() { public void paintObject(PaintObjectEvent event) { GC gc = event.gc; StyleRange style = event.style; int start = style.start; for (int i = 0; i < offsets.length; i++) { int offset = offsets[i]; if (start == offset) { Image image = images[i]; int x = event.x; int y = event.y + event.ascent - style.metrics.ascent; gc.drawImage(image, x, y); } } } }); Button button = new Button(shell, SWT.PUSH); button.setText("Add Image"); button.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); button.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { FileDialog dialog = new FileDialog(shell); String filename = dialog.open(); if (filename != null) { try { Image image = new Image(display, filename); int offset = styledText.getCaretOffset(); styledText.replaceTextRange(offset, 0, "\uFFFC"); int index = 0; while (index < offsets.length) { if (offsets[index] == -1 && images[index] == null) break; index++; } if (index == offsets.length) { int[] tmpOffsets = new int[index + 1]; System.arraycopy(offsets, 0, tmpOffsets, 0, offsets.length); offsets = tmpOffsets; Image[] tmpImages = new Image[index + 1]; System.arraycopy(images, 0, tmpImages, 0, images.length); images = tmpImages; } offsets[index] = offset; images[index] = image; addImage(image, offset); } catch (Exception e) { e.printStackTrace(); } } } }); shell.setSize(400, 400); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } for (int i = 0; i < images.length; i++) { Image image = images[i]; if (image != null && !image.isDisposed()) { image.dispose(); } } display.dispose(); }
}</source>
StyledText: use gradient background
<source lang="java">
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
//package org.eclipse.swt.snippets; /*
* SWT StyledText snippet: use gradient background. * * For a list of all SWT example snippets see * http://www.eclipse.org/swt/snippets/ * * @since 3.2 */
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; public class StyledTextGradientBackground {
static String text = "Plans do not materialize out of nowhere, nor are they entirely static. To ensure the planning process is " + "transparent and open to the entire Eclipse community, we (the Eclipse PMC) post plans in an embryonic " + "form and revise them throughout the release cycle. \n" + "The first part of the plan deals with the important matters of release deliverables, release milestones, target " + "operating environments, and release-to-release compatibility. These are all things that need to be clear for " + "any release, even if no features were to change. \n"; static Image oldImage; public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); shell.setLayout(new FillLayout()); final StyledText styledText = new StyledText(shell, SWT.WRAP | SWT.BORDER); styledText.setText(text); FontData data = display.getSystemFont().getFontData()[0]; Font font = new Font(display, data.getName(), 16, SWT.BOLD); styledText.setFont(font); styledText.setForeground(display.getSystemColor(SWT.COLOR_BLUE)); styledText.addListener(SWT.Resize, new Listener() { public void handleEvent(Event event) { Rectangle rect = styledText.getClientArea(); Image newImage = new Image(display, 1, Math.max(1, rect.height)); GC gc = new GC(newImage); gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW)); gc.fillGradientRectangle(rect.x, rect.y, 1, rect.height, true); gc.dispose(); styledText.setBackgroundImage(newImage); if (oldImage != null) oldImage.dispose(); oldImage = newImage; } }); shell.setSize(700, 400); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } if (oldImage != null) oldImage.dispose(); font.dispose(); display.dispose(); }
}</source>
Understanding the Repercussions
When you use a LineStyleListener , you shouldn"t use the following API calls:
- getStyleRangeAtOffset(int offset)
- StyleRange[] getStyleRanges()
- void replaceStyleRanges(int start, int length, StyleRange[] ranges)
- void setStyleRange(StyleRange range)
- void setStyleRanges(StyleRange[] ranges)
Mixing these API calls with a LineStyleListener is unsupported. (The Definitive Guide to SWT and JFace by Robert Harris and Rob Warner Apress 2004 )
Using the Clipboard
<source lang="java">
import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class StyledTextClipboard {
public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display); StyledText text1 = new StyledText(shell, SWT.V_SCROLL | SWT.WRAP | SWT.BORDER); StyledText text2 = new StyledText(shell, SWT.V_SCROLL | SWT.WRAP | SWT.BORDER); text1.setText("1234567"); text1.setSelectionRange(2,4); text1.cut(); text2.paste(); text1.setBounds(10,10,100,100); text2.setBounds(10,300,100,100); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); }
}</source>