Java Tutorial/SWT/StyledText

Материал из Java эксперт
Перейти к: навигация, поиск

A simple editor

//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]);
    }
  }
}





Create a StyledText that scrolls vertically, wraps text, and displays a border:

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();
  }
}





Creating a StyledText Widget

StyledText(Composite parent, int style)



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

/*******************************************************************************
 * 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();
  }
}





Getting Statistics: Caret Offset, Total Lines of Text, Total Characters and Current Line

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();
  }
}





Limit the number of characters that the StyledText accepts

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();
  }
}





Make a StyledText read-only

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();
  }
}





Print StyledText out

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();
  }
}





Print to the default printer in a separate thread

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();
  }
}





Replace Text Range

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();
  }
}





Set page format for printing

  1. print the name of the file on top of each page,
  2. print the page number at the bottom of each page,
  3. print the word "Confidential" in the lower-right corner,
  4. print the text in the appropriate colors and styles



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();
  }
}





StyledText: embed controls

/*******************************************************************************
 * 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();
  }
}





StyledText: embed images

/*******************************************************************************
 * 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();
  }
}





StyledText: use gradient background

/*******************************************************************************
 * 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();
  }
}





Understanding the Repercussions

When you use a LineStyleListener , you shouldn"t use the following API calls:

  1. getStyleRangeAtOffset(int offset)
  2. StyleRange[] getStyleRanges()
  3. void replaceStyleRanges(int start, int length, StyleRange[] ranges)
  4. void setStyleRange(StyleRange range)
  5. 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

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();
  }
}