diff dwt/custom/StyledText.d @ 41:6337764516f1

Sync dwt/custom with dwt-linux (took copy of complete folder)
author Frank Benoit <benoit@tionex.de>
date Tue, 07 Oct 2008 16:29:55 +0200
parents db5a898b2119
children 07399639c0c8
line wrap: on
line diff
--- a/dwt/custom/StyledText.d	Tue Oct 07 14:41:31 2008 +0200
+++ b/dwt/custom/StyledText.d	Tue Oct 07 16:29:55 2008 +0200
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * Copyright (c) 2000, 2008 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
@@ -7,39 +7,109 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
  *******************************************************************************/
-module dwt.custom;
+module dwt.custom.StyledText;
 
 
-import java.util.*;
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.DWTException;
+import dwt.accessibility.ACC;
+import dwt.accessibility.Accessible;
+import dwt.accessibility.AccessibleAdapter;
+import dwt.accessibility.AccessibleControlAdapter;
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleEvent;
+import dwt.accessibility.AccessibleTextAdapter;
+import dwt.accessibility.AccessibleTextEvent;
+import dwt.dnd.Clipboard;
+import dwt.dnd.DND;
+import dwt.dnd.RTFTransfer;
+import dwt.dnd.TextTransfer;
+import dwt.dnd.Transfer;
+import dwt.events.ModifyListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.VerifyListener;
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.graphics.GlyphMetrics;
+import dwt.graphics.Image;
+import dwt.graphics.Device;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.graphics.Resource;
+import dwt.graphics.TextLayout;
+import dwt.internal.BidiUtil;
+import dwt.internal.Compatibility;
+import dwt.printing.Printer;
+import dwt.printing.PrinterData;
+import dwt.widgets.Canvas;
+import dwt.widgets.Caret;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.IME;
+import dwt.widgets.Label;
+import dwt.widgets.Listener;
+import dwt.widgets.ScrollBar;
+import dwt.widgets.TypedListener;
+import dwt.custom.StyledTextContent;
+import dwt.custom.TextChangeListener;
+import dwt.custom.StyledTextRenderer;
+import dwt.custom.StyledTextPrintOptions;
+import dwt.custom.ExtendedModifyListener;
+import dwt.custom.BidiSegmentListener;
+import dwt.custom.LineBackgroundListener;
+import dwt.custom.LineStyleListener;
+import dwt.custom.PaintObjectListener;
+import dwt.custom.VerifyKeyListener;
+import dwt.custom.MovementListener;
+import dwt.custom.Bullet;
+import dwt.custom.StyledTextEvent;
+import dwt.custom.StyleRange;
+import dwt.custom.TextChangedEvent;
+import dwt.custom.TextChangingEvent;
+import dwt.custom.DefaultContent;
+import dwt.custom.StyledTextDropTargetEffect;
+import dwt.custom.StyledTextListener;
+import dwt.custom.ST;
+import dwt.dwthelper.Runnable;
 
-import dwt.*;
-import dwt.accessibility.*;
-import dwt.dnd.*;
-import dwt.events.*;
-import dwt.graphics.*;
-import dwt.internal.*;
-import dwt.printing.*;
-import dwt.widgets.*;
+static import tango.text.Text;
+static import tango.text.Util;
+static import tango.io.model.IFile;
+static import tango.text.convert.Utf;
+import tango.util.Convert;
+import dwt.dwthelper.utils;
 
-/**
- * A StyledText is an editable user interface object that displays lines 
- * of text.  The following style attributes can be defined for the text: 
+alias tango.text.Text.Text!(char) StringBuffer;
+
+/**
+ * A StyledText is an editable user interface object that displays lines
+ * of text.  The following style attributes can be defined for the text:
  * <ul>
- * <li>foreground color 
+ * <li>foreground color
  * <li>background color
  * <li>font style (bold, italic, bold-italic, regular)
  * <li>underline
  * <li>strikeout
  * </ul>
  * <p>
- * In addition to text style attributes, the background color of a line may 
+ * In addition to text style attributes, the background color of a line may
  * be specified.
  * </p><p>
- * There are two ways to use this widget when specifying text style information.  
- * You may use the API that is defined for StyledText or you may define your own 
- * LineStyleListener.  If you define your own listener, you will be responsible 
- * for maintaining the text style information for the widget.  IMPORTANT: You may 
+ * There are two ways to use this widget when specifying text style information.
+ * You may use the API that is defined for StyledText or you may define your own
+ * LineStyleListener.  If you define your own listener, you will be responsible
+ * for maintaining the text style information for the widget.  IMPORTANT: You may
  * not define your own listener and use the StyledText API.  The following
  * StyledText API is not supported if you have defined a LineStyleListener:
  * <ul>
@@ -51,11 +121,11 @@
  * </ul>
  * </p><p>
  * There are two ways to use this widget when specifying line background colors.
- * You may use the API that is defined for StyledText or you may define your own 
- * LineBackgroundListener.  If you define your own listener, you will be responsible 
- * for maintaining the line background color information for the widget.  
- * IMPORTANT: You may not define your own listener and use the StyledText API.  
- * The following StyledText API is not supported if you have defined a 
+ * You may use the API that is defined for StyledText or you may define your own
+ * LineBackgroundListener.  If you define your own listener, you will be responsible
+ * for maintaining the line background color information for the widget.
+ * IMPORTANT: You may not define your own listener and use the StyledText API.
+ * The following StyledText API is not supported if you have defined a
  * LineBackgroundListener:
  * <ul>
  * <li>getLineBackground(int)
@@ -64,7 +134,7 @@
  * </p><p>
  * The content implementation for this widget may also be user-defined.  To do so,
  * you must implement the StyledTextContent interface and use the StyledText API
- * setContent(StyledTextContent) to initialize the widget. 
+ * setContent(StyledTextContent) to initialize the widget.
  * </p><p>
  * <dl>
  * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
@@ -73,31 +143,37 @@
  * </p><p>
  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
  * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#styledtext">StyledText snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Examples: CustomControlExample, TextEditor</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
  */
 public class StyledText : Canvas {
-    static final char TAB = '\t';
-    static final String PlatformLineDelimiter = System.getProperty("line.separator");
-    static final int BIDI_CARET_WIDTH = 3;
-    static final int DEFAULT_WIDTH  = 64;
-    static final int DEFAULT_HEIGHT = 64;
-    static final int V_SCROLL_RATE = 50;
-    static final int H_SCROLL_RATE = 10;
-    
-    static final int ExtendedModify = 3000;
-    static final int LineGetBackground = 3001;
-    static final int LineGetStyle = 3002;
-    static final int TextChanging = 3003;
-    static final int TextSet = 3004;
-    static final int VerifyKey = 3005;
-    static final int TextChanged = 3006;
-    static final int LineGetSegments = 3007;
-    static final int PaintObject = 3008;
-    static final int WordNext = 3009;
-    static final int WordPrevious = 3010;
-    
-    static final int PREVIOUS_OFFSET_TRAILING = 0;
-    static final int OFFSET_LEADING = 1;
-    
+    alias Canvas.computeSize computeSize;
+
+    static const char TAB = '\t';
+    static const String PlatformLineDelimiter = tango.io.model.IFile.FileConst.NewlineString;
+    static const int BIDI_CARET_WIDTH = 3;
+    static const int DEFAULT_WIDTH  = 64;
+    static const int DEFAULT_HEIGHT = 64;
+    static const int V_SCROLL_RATE = 50;
+    static const int H_SCROLL_RATE = 10;
+
+    static const int ExtendedModify = 3000;
+    static const int LineGetBackground = 3001;
+    static const int LineGetStyle = 3002;
+    static const int TextChanging = 3003;
+    static const int TextSet = 3004;
+    static const int VerifyKey = 3005;
+    static const int TextChanged = 3006;
+    static const int LineGetSegments = 3007;
+    static const int PaintObject = 3008;
+    static const int WordNext = 3009;
+    static const int WordPrevious = 3010;
+
+    static const int PREVIOUS_OFFSET_TRAILING = 0;
+    static const int OFFSET_LEADING = 1;
+
     Color selectionBackground;  // selection background color
     Color selectionForeground;  // selection foreground color
     StyledTextContent content;          // native content (default or user specified)
@@ -118,30 +194,30 @@
     int columnX;                        // keep track of the horizontal caret position when changing lines/pages. Fixes bug 5935
     int caretOffset = 0;
     int caretAlignment;
-    Point selection = new Point(0, 0);  // x and y are start and end caret offsets of selection
+    Point selection;                    // x and y are start and end caret offsets of selection
     Point clipboardSelection;           // x and y are start and end caret offsets of previous selection
     int selectionAnchor;                // position of selection anchor. 0 based offset from beginning of text
     Point doubleClickSelection;         // selection after last mouse double click
     bool editable = true;
     bool wordWrap = false;
-    bool doubleClickEnabled = true; // see getDoubleClickEnabled 
-    bool overwrite = false;         // insert/overwrite edit mode
+    bool doubleClickEnabled = true;  // see getDoubleClickEnabled
+    bool overwrite = false;          // insert/overwrite edit mode
     int textLimit = -1;                 // limits the number of characters the user can type in the widget. Unlimited by default.
-    Hashtable keyActionMap = new Hashtable();
+    int[int] keyActionMap;
     Color background = null;            // workaround for bug 4791
     Color foreground = null;            //
     Clipboard clipboard;
     int clickCount;
     int autoScrollDirection = DWT.NULL; // the direction of autoscrolling (up, down, right, left)
     int autoScrollDistance = 0;
-    int lastTextChangeStart;            // cache data of the 
-    int lastTextChangeNewLineCount;     // last text changing 
-    int lastTextChangeNewCharCount;     // event for use in the 
+    int lastTextChangeStart;            // cache data of the
+    int lastTextChangeNewLineCount;     // last text changing
+    int lastTextChangeNewCharCount;     // event for use in the
     int lastTextChangeReplaceLineCount; // text changed handler
     int lastTextChangeReplaceCharCount;
     int lastLineBottom;                 // the bottom pixel of the last line been replaced
-    bool isMirrored;
-    bool bidiColoring = false;      // apply the BIDI algorithm on text segments of the same color
+    bool isMirrored_;
+    bool bidiColoring = false;       // apply the BIDI algorithm on text segments of the same color
     Image leftCaretBitmap = null;
     Image rightCaretBitmap = null;
     int caretDirection = DWT.NULL;
@@ -149,32 +225,32 @@
     Caret defaultCaret = null;
     bool updateCaretDirection = true;
     bool fixedLineHeight;
-    bool dragDetect = true;
+    bool dragDetect_ = true;
     IME ime;
-    
+
     int alignment;
     bool justify;
     int indent;
     int lineSpacing;
 
-    final static bool IS_CARBON, IS_GTK, IS_MOTIF;
-    static {
+    const static bool IS_CARBON, IS_GTK, IS_MOTIF;
+    static this(){
         String platform = DWT.getPlatform();
-        IS_CARBON = "carbon".opEquals(platform);
-        IS_GTK = "gtk".opEquals(platform);
-        IS_MOTIF = "motif".opEquals(platform);
+        IS_CARBON = ("carbon" == platform);
+        IS_GTK    = ("gtk"    == platform);
+        IS_MOTIF  = ("motif"  == platform);
     }
 
     /**
      * The Printing class : printing of a range of text.
-     * An instance of <code>Printing</code> is returned in the 
-     * StyledText#print(Printer) API. The run() method may be 
+     * An instance of <code>Printing</code> is returned in the
+     * StyledText#print(Printer) API. The run() method may be
      * invoked from any thread.
      */
     static class Printing : Runnable {
-        final static int LEFT = 0;                      // left aligned header/footer segment
-        final static int CENTER = 1;                    // centered header/footer segment
-        final static int RIGHT = 2;                     // right aligned header/footer segment
+        const static int LEFT = 0;                      // left aligned header/footer segment
+        const static int CENTER = 1;                    // centered header/footer segment
+        const static int RIGHT = 2;                     // right aligned header/footer segment
 
         Printer printer;
         StyledTextRenderer printerRenderer;
@@ -182,7 +258,7 @@
         Rectangle clientArea;
         FontData fontData;
         Font printerFont;
-        Hashtable resources;
+        Resource[Resource] resources;
         int tabLength;
         GC gc;                                          // printer GC
         int pageWidth;                                  // width of a printer page in pixels
@@ -190,30 +266,30 @@
         int endPage;                                    // last page to print
         int startLine;                                  // first (wrapped) line to print
         int endLine;                                    // last (wrapped) line to print
-        bool singleLine;                                // widget single line mode
+        bool singleLine;                             // widget single line mode
         Point selection = null;                 // selected text
-        bool mirrored;                      // indicates the printing gc should be mirrored
+        bool mirrored;                       // indicates the printing gc should be mirrored
         int lineSpacing;
         int printMargin;
 
     /**
      * Creates an instance of <code>Printing</code>.
-     * Copies the widget content and rendering data that needs 
+     * Copies the widget content and rendering data that needs
      * to be requested from listeners.
      * </p>
      * @param parent StyledText widget to print.
      * @param printer printer device to print on.
      * @param printOptions print options
-     */     
+     */
     this(StyledText styledText, Printer printer, StyledTextPrintOptions printOptions) {
         this.printer = printer;
         this.printOptions = printOptions;
         this.mirrored = (styledText.getStyle() & DWT.MIRRORED) !is 0;
         singleLine = styledText.isSingleLine();
         startPage = 1;
-        endPage = Integer.MAX_VALUE;
+        endPage = int.max;
         PrinterData data = printer.getPrinterData();
-        if (data.scope is PrinterData.PAGE_RANGE) {
+        if (data.scope_ is PrinterData.PAGE_RANGE) {
             startPage = data.startPage;
             endPage = data.endPage;
             if (endPage < startPage) {
@@ -221,7 +297,7 @@
                 endPage = startPage;
                 startPage = temp;
             }
-        } else if (data.scope is PrinterData.SELECTION) {
+        } else if (data.scope_ is PrinterData.SELECTION) {
             selection = styledText.getSelectionRange();
         }
         printerRenderer = new StyledTextRenderer(printer, null);
@@ -231,7 +307,7 @@
     /**
      * Caches all line data that needs to be requested from a listener.
      * </p>
-     * @param printerContent <code>StyledTextContent</code> to request 
+     * @param printerContent <code>StyledTextContent</code> to request
      *  line data for.
      */
     void cacheLineData(StyledText styledText) {
@@ -252,7 +328,7 @@
                 if (styledText.isBidi()) {
                     int[] segments = styledText.getBidiSegments(lineOffset, line);
                     printerRenderer.setLineSegments(i, 1, segments);
-                }           
+                }
                 event = styledText.getLineStyleData(lineOffset, line);
                 if (event !is null) {
                     printerRenderer.setLineIndent(i, 1, event.indent);
@@ -268,15 +344,18 @@
         }
         Point screenDPI = styledText.getDisplay().getDPI();
         Point printerDPI = printer.getDPI();
-        resources = new Hashtable ();
+        resources = null;
         for (int i = 0; i < lineCount; i++) {
             Color color = printerRenderer.getLineBackground(i, null);
             if (color !is null) {
                 if (printOptions.printLineBackground) {
-                    Color printerColor = cast(Color)resources.get(color);
-                    if (printerColor is null) {
+                    Color printerColor;
+                    if ( auto p = color in resources ) {
+                        printerColor = cast(Color)*p;
+                    }
+                    else {
                         printerColor = new Color (printer, color.getRGB());
-                        resources.put(color, printerColor); 
+                        resources[color]=printerColor;
                     }
                     printerRenderer.setLineBackground(i, 1, printerColor);
                 } else {
@@ -293,20 +372,26 @@
             StyleRange style = styles[i];
             Font font = style.font;
             if (style.font !is null) {
-                Font printerFont = cast(Font)resources.get(font);
-                if (printerFont is null) {
+                Font printerFont;
+                if ( auto p = font in resources ) {
+                    printerFont = cast(Font)*p;
+                }
+                else {
                     printerFont = new Font (printer, font.getFontData());
-                    resources.put(font, printerFont);                   
+                    resources[font]= printerFont;
                 }
                 style.font = printerFont;
             }
             Color color = style.foreground;
             if (color !is null) {
-                Color printerColor = cast(Color)resources.get(color);
                 if (printOptions.printTextForeground) {
-                    if (printerColor is null) {
+                    Color printerColor;
+                    if ( auto p = color in resources ) {
+                        printerColor = cast(Color)*p;
+                    }
+                    else {
                         printerColor = new Color (printer, color.getRGB());
-                        resources.put(color, printerColor); 
+                        resources[color]=printerColor;
                     }
                     style.foreground = printerColor;
                 } else {
@@ -315,11 +400,14 @@
             }
             color = style.background;
             if (color !is null) {
-                Color printerColor = cast(Color)resources.get(color);
                 if (printOptions.printTextBackground) {
-                    if (printerColor is null) {
+                    Color printerColor;
+                    if ( auto p = color in resources ) {
+                        printerColor = cast(Color)*p;
+                    }
+                    else {
                         printerColor = new Color (printer, color.getRGB());
-                        resources.put(color, printerColor); 
+                        resources[color]=printerColor;
                     }
                     style.background = printerColor;
                 } else {
@@ -370,14 +458,10 @@
             gc.dispose();
             gc = null;
         }
-        if (resources !is null) {
-            Enumeration enumeration = resources.elements();         
-            while (enumeration.hasMoreElements()) {
-                Resource resource = cast(Resource) enumeration.nextElement();
-                resource.dispose();
-            }
-            resources = null;
-        }
+        foreach( resource; resources.values ){
+            resource.dispose();
+        }
+        resources = null;
         if (printerFont !is null) {
             printerFont.dispose();
             printerFont = null;
@@ -390,15 +474,15 @@
     void init_() {
         Rectangle trim = printer.computeTrim(0, 0, 0, 0);
         Point dpi = printer.getDPI();
-        
-        printerFont = new Font(printer, fontData.getName(), fontData.getHeight(), DWT.NORMAL);
+
+        printerFont = new Font( cast(Device)printer, fontData.getName(), fontData.getHeight(), DWT.NORMAL);
         clientArea = printer.getClientArea();
         pageWidth = clientArea.width;
         // one inch margin around text
-        clientArea.x = dpi.x + trim.x;              
+        clientArea.x = dpi.x + trim.x;
         clientArea.y = dpi.y + trim.y;
         clientArea.width -= (clientArea.x + trim.width);
-        clientArea.height -= (clientArea.y + trim.height); 
+        clientArea.height -= (clientArea.y + trim.height);
 
         int style = mirrored ? DWT.RIGHT_TO_LEFT : DWT.LEFT_TO_RIGHT;
         gc = new GC(printer, style);
@@ -412,16 +496,16 @@
         if (printOptions.footer !is null) {
             clientArea.height -= lineHeight * 2;
         }
-        
+
         // TODO not wrapped
         StyledTextContent content = printerRenderer.content;
         startLine = 0;
         endLine = singleLine ? 0 : content.getLineCount() - 1;
         PrinterData data = printer.getPrinterData();
-        if (data.scope is PrinterData.PAGE_RANGE) {
+        if (data.scope_ is PrinterData.PAGE_RANGE) {
             int pageSize = clientArea.height / lineHeight;//WRONG
             startLine = (startPage - 1) * pageSize;
-        } else if (data.scope is PrinterData.SELECTION) {
+        } else if (data.scope_ is PrinterData.SELECTION) {
             startLine = content.getLineAtOffset(selection.x);
             if (selection.y > 0) {
                 endLine = content.getLineAtOffset(selection.x + selection.y - 1);
@@ -477,7 +561,7 @@
             }
             TextLayout layout = printerRenderer.getTextLayout(i, orientation, width, lineSpacing);
             Color lineBackground = printerRenderer.getLineBackground(i, background);
-            int paragraphBottom = paintY + layout.getBounds().height; 
+            int paragraphBottom = paintY + layout.getBounds().height;
             if (paragraphBottom <= pageBottom) {
                 //normal case, the whole paragraph fits in the current page
                 printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
@@ -507,7 +591,7 @@
                     printLine(paintX, paintY, gc, foreground, lineBackground, layout, printLayout, i);
                     gc.setClipping(cast(Rectangle)null);
                     printDecoration(page, false, printLayout);
-                    printer.endPage();                  
+                    printer.endPage();
                     page++;
                     if (page <= endPage) {
                         printer.startPage();
@@ -532,7 +616,7 @@
     }
     /**
      * Print header or footer decorations.
-     * 
+     *
      * @param page page number to print, if specified in the StyledTextPrintOptions header or footer.
      * @param header true = print the header, false = print the footer
      */
@@ -541,16 +625,16 @@
         if (text is null) return;
         int lastSegmentIndex = 0;
         for (int i = 0; i < 3; i++) {
-            int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
+            int segmentIndex = text.indexOf( StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
             String segment;
-            if (segmentIndex is -1) {
+            if (segmentIndex is -1 ) {
                 segment = text.substring(lastSegmentIndex);
                 printDecorationSegment(segment, i, page, header, layout);
                 break;
             } else {
                 segment = text.substring(lastSegmentIndex, segmentIndex);
                 printDecorationSegment(segment, i, page, header, layout);
-                lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
+                lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length;
             }
         }
     }
@@ -558,22 +642,22 @@
      * Print one segment of a header or footer decoration.
      * Headers and footers have three different segments.
      * One each for left aligned, centered, and right aligned text.
-     * 
+     *
      * @param segment decoration segment to print
-     * @param alignment alignment of the segment. 0=left, 1=center, 2=right 
+     * @param alignment alignment of the segment. 0=left, 1=center, 2=right
      * @param page page number to print, if specified in the decoration segment.
      * @param header true = print the header, false = print the footer
      */
-    void printDecorationSegment(String segment, int alignment, int page, bool header, TextLayout layout) {      
+    void printDecorationSegment(String segment, int alignment, int page, bool header, TextLayout layout) {
         int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
-        if (pageIndex !is -1) {
-            int pageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
+        if (pageIndex !is -1 ) {
+            int pageTagLength = StyledTextPrintOptions.PAGE_TAG.length;
             StringBuffer buffer = new StringBuffer(segment.substring (0, pageIndex));
             buffer.append (page);
             buffer.append (segment.substring(pageIndex + pageTagLength));
-            segment = buffer.toString();
-        }
-        if (segment.length() > 0) {
+            segment = buffer.toString().dup;
+        }
+        if (segment.length > 0) {
             layout.setText(segment);
             int segmentWidth = layout.getBounds().width;
             int segmentHeight = printerRenderer.getLineHeight();
@@ -598,7 +682,7 @@
             Rectangle rect = layout.getBounds();
             gc.setBackground(background);
             gc.fillRectangle(x, y, rect.width, rect.height);
-            
+
 //          int lineCount = layout.getLineCount();
 //          for (int i = 0; i < lineCount; i++) {
 //              Rectangle rect = layout.getLineBounds(i);
@@ -620,7 +704,7 @@
                     printLayout.setText("");
                 }
             } else {
-                printLayout.setText(String.valueOf(index));
+                printLayout.setText(to!(String)(index));
             }
             int paintX = x - printMargin - printLayout.getBounds().width;
             printLayout.draw(gc, paintX, y);
@@ -639,64 +723,66 @@
             jobName = "Printing";
         }
         if (printer.startJob(jobName)) {
-            init();
+            init_();
             print();
             dispose();
             printer.endJob();
         }
-    }   
+    }
     }
     /**
      * The <code>RTFWriter</code> class is used to write widget content as
-     * rich text. The implementation complies with the RTF specification 
+     * rich text. The implementation complies with the RTF specification
      * version 1.5.
      * <p>
-     * toString() is guaranteed to return a valid RTF String only after 
-     * close() has been called. 
+     * toString() is guaranteed to return a valid RTF string only after
+     * close() has been called.
      * </p><p>
      * Whole and partial lines and line breaks can be written. Lines will be
-     * formatted using the styles queried from the LineStyleListener, if 
+     * formatted using the styles queried from the LineStyleListener, if
      * set, or those set directly in the widget. All styles are applied to
-     * the RTF stream like they are rendered by the widget. In addition, the 
+     * the RTF stream like they are rendered by the widget. In addition, the
      * widget font name and size is used for the whole text.
      * </p>
      */
     class RTFWriter : TextWriter {
-        static final int DEFAULT_FOREGROUND = 0;
-        static final int DEFAULT_BACKGROUND = 1;
-        Vector colorTable, fontTable;
+
+        alias TextWriter.write write;
+
+        static const int DEFAULT_FOREGROUND = 0;
+        static const int DEFAULT_BACKGROUND = 1;
+        Color[] colorTable;
+        Font[] fontTable;
         bool WriteUnicode;
-        
+
     /**
      * Creates a RTF writer that writes content starting at offset "start"
-     * in the document.  <code>start</code> and <code>length</code>can be set to specify partial 
+     * in the document.  <code>start</code> and <code>length</code>can be set to specify partial
      * lines.
      *
-     * @param start start offset of content to write, 0 based from 
+     * @param start start offset of content to write, 0 based from
      *  beginning of document
      * @param length length of content to write
      */
     public this(int start, int length) {
         super(start, length);
-        colorTable = new Vector();
-        fontTable = new Vector();
-        colorTable.addElement(getForeground());
-        colorTable.addElement(getBackground());
-        fontTable.addElement(getFont());
+        colorTable ~= getForeground();
+        colorTable ~= getBackground();
+        fontTable ~= getFont();
         setUnicode();
     }
     /**
      * Closes the RTF writer. Once closed no more content can be written.
-     * <b>NOTE:</b>  <code>toString()</code> does not return a valid RTF String until 
+     * <b>NOTE:</b>  <code>toString()</code> does not return a valid RTF string until
      * <code>close()</code> has been called.
      */
-    public void close() {
+    public override void close() {
         if (!isClosed()) {
             writeHeader();
             write("\n}}\0");
             super.close();
         }
-    }   
+    }
     /**
      * Returns the index of the specified color in the RTF color table.
      *
@@ -707,10 +793,16 @@
      */
     int getColorIndex(Color color, int defaultIndex) {
         if (color is null) return defaultIndex;
-        int index = colorTable.indexOf(color);
+        int index = -1;
+        foreach( i, col; colorTable ){
+            if( col == color ){
+                index = i;
+                break;
+            }
+        }
         if (index is -1) {
-            index = colorTable.size();
-            colorTable.addElement(color);
+            index = colorTable.length;
+            colorTable ~= color;
         }
         return index;
     }
@@ -723,10 +815,16 @@
      *  or "defaultIndex" if "color" is null.
      */
     int getFontIndex(Font font) {
-        int index = fontTable.indexOf(font);
+        int index = -1;
+        foreach( i, f; colorTable ){
+            if( f == font ){
+                index = i;
+                break;
+            }
+        }
         if (index is -1) {
-            index = fontTable.size();
-            fontTable.addElement(font);
+            index = fontTable.length;
+            fontTable ~= font;
         }
         return index;
     }
@@ -735,68 +833,72 @@
      * Don't write Unicode RTF on Windows 95/98/ME or NT.
      */
     void setUnicode() {
-        final String Win95 = "windows 95";
-        final String Win98 = "windows 98";
-        final String WinME = "windows me";      
-        final String WinNT = "windows nt";
-        String osName = System.getProperty("os.name").toLowerCase();
-        String osVersion = System.getProperty("os.version");
-        int majorVersion = 0;
-        
-        if (osName.startsWith(WinNT) && osVersion !is null) {
-            int majorIndex = osVersion.indexOf('.');
-            if (majorIndex !is -1) {
-                osVersion = osVersion.substring(0, majorIndex);
-                try {
-                    majorVersion = Integer.parseInt(osVersion);
-                } catch (NumberFormatException exception) {
-                    // ignore exception. version number remains unknown.
-                    // will write without Unicode
-                }
-            }
-        }
-        WriteUnicode =  !osName.startsWith(Win95) &&
-                        !osName.startsWith(Win98) &&
-                        !osName.startsWith(WinME) &&
-                        (!osName.startsWith(WinNT) || majorVersion > 4);
+//         const String Win95 = "windows 95";
+//         const String Win98 = "windows 98";
+//         const String WinME = "windows me";
+//         const String WinNT = "windows nt";
+//         String osName = System.getProperty("os.name").toLowerCase();
+//         String osVersion = System.getProperty("os.version");
+//         int majorVersion = 0;
+//
+//         if (osName.startsWith(WinNT) && osVersion !is null) {
+//             int majorIndex = osVersion.indexOf('.');
+//             if (majorIndex !is -1) {
+//                 osVersion = osVersion.substring(0, majorIndex);
+//                 try {
+//                     majorVersion = Integer.parseInt(osVersion);
+//                 } catch (NumberFormatException exception) {
+//                     // ignore exception. version number remains unknown.
+//                     // will write without Unicode
+//                 }
+//             }
+//         }
+//         WriteUnicode =  !osName.startsWith(Win95) &&
+//                         !osName.startsWith(Win98) &&
+//                         !osName.startsWith(WinME) &&
+//                         (!osName.startsWith(WinNT) || majorVersion > 4);
+        WriteUnicode = true; // we are on linux-gtk
     }
     /**
-     * Appends the specified segment of "String" to the RTF data.
+     * Appends the specified segment of "string" to the RTF data.
      * Copy from <code>start</code> up to, but excluding, <code>end</code>.
      *
-     * @param String String to copy a segment from. Must not contain
+     * @param string string to copy a segment from. Must not contain
      *  line breaks. Line breaks should be written using writeLineDelimiter()
      * @param start start offset of segment. 0 based.
      * @param end end offset of segment
      */
-    void write(String String, int start, int end) {
-        for (int index = start; index < end; index++) {
-            char ch = String.charAt(index);
+    void write(String string, int start, int end) {
+        start = 0;
+        end = string.length;
+        int incr = 1;
+        for (int index = start; index < end; index+=incr) {
+            dchar ch = firstCodePoint( string[index .. $], incr );
             if (ch > 0xFF && WriteUnicode) {
-                // write the sub String from the last escaped character 
+                // write the sub string from the last escaped character
                 // to the current one. Fixes bug 21698.
                 if (index > start) {
-                    write(String.substring(start, index));
+                    write( string[start .. index ] );
                 }
                 write("\\u");
-                write(Integer.toString(cast(short) ch));
+                write( to!(String)( cast(short)ch ));
                 write(' ');                     // control word delimiter
-                start = index + 1;
+                start = index + incr;
             } else if (ch is '}' || ch is '{' || ch is '\\') {
-                // write the sub String from the last escaped character 
+                // write the sub string from the last escaped character
                 // to the current one. Fixes bug 21698.
                 if (index > start) {
-                    write(String.substring(start, index));
+                    write(string[start .. index]);
                 }
                 write('\\');
-                write(ch);
+                write(cast(char)ch); // ok because one of {}\
                 start = index + 1;
             }
         }
         // write from the last escaped character to the end.
         // Fixes bug 21698.
         if (start < end) {
-            write(String.substring(start, end));
+            write(string[ start .. end]);
         }
     }
     /**
@@ -806,28 +908,31 @@
         StringBuffer header = new StringBuffer();
         FontData fontData = getFont().getFontData()[0];
         header.append("{\\rtf1\\ansi");
-        // specify code page, necessary for copy to work in bidi 
+        // specify code page, necessary for copy to work in bidi
         // systems that don't support Unicode RTF.
-        String cpg = System.getProperty("file.encoding").toLowerCase();
+        // PORTING_TODO: String cpg = System.getProperty("file.encoding").toLowerCase();
+        String cpg = "UTF16";
+        /+
         if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
             cpg = cpg.substring(2, cpg.length());
             header.append("\\ansicpg");
             header.append(cpg);
         }
+        +/
         header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
         header.append(fontData.getName());
         header.append(";");
-        for (int i = 1; i < fontTable.size(); i++) {
+        for (int i = 1; i < fontTable.length; i++) {
             header.append("\\f");
             header.append(i);
             header.append(" ");
-            FontData fd = (cast(Font)fontTable.elementAt(i)).getFontData()[0];
+            FontData fd = (cast(Font)fontTable[i]).getFontData()[0];
             header.append(fd.getName());
-            header.append(";");         
+            header.append(";");
         }
         header.append("}}\n{\\colortbl");
-        for (int i = 0; i < colorTable.size(); i++) {
-            Color color = cast(Color) colorTable.elementAt(i);
+        for (int i = 0; i < colorTable.length; i++) {
+            Color color = cast(Color) colorTable[i];
             header.append("\\red");
             header.append(color.getRed());
             header.append("\\green");
@@ -836,7 +941,7 @@
             header.append(color.getBlue());
             header.append(";");
         }
-        // some RTF readers ignore the deff0 font tag. Explicitly 
+        // some RTF readers ignore the deff0 font tag. Explicitly
         // set the font for the whole document to work around this.
         header.append("}\n{\\f0\\fs");
         // font size is specified in half points
@@ -845,20 +950,20 @@
         write(header.toString(), 0);
     }
     /**
-     * Appends the specified line text to the RTF data.  Lines will be formatted 
-     * using the styles queried from the LineStyleListener, if set, or those set 
+     * Appends the specified line text to the RTF data.  Lines will be formatted
+     * using the styles queried from the LineStyleListener, if set, or those set
      * directly in the widget.
      *
      * @param line line text to write as RTF. Must not contain line breaks
      *  Line breaks should be written using writeLineDelimiter()
-     * @param lineOffset offset of the line. 0 based from the start of the 
-     *  widget document. Any text occurring before the start offset or after the 
+     * @param lineOffset offset of the line. 0 based from the start of the
+     *  widget document. Any text occurring before the start offset or after the
      *  end offset specified during object creation is ignored.
      * @exception DWTException <ul>
      *   <li>ERROR_IO when the writer is closed.</li>
      * </ul>
      */
-    public void writeLine(String line, int lineOffset) {
+    public override void writeLine(String line, int lineOffset) {
         if (isClosed()) {
             DWT.error(DWT.ERROR_IO);
         }
@@ -876,12 +981,12 @@
             styles = event.styles;
         } else {
             lineAlignment = renderer.getLineAlignment(lineIndex, alignment);
-            lineIndent =  renderer.getLineIndent(lineIndex, indent);            
+            lineIndent =  renderer.getLineIndent(lineIndex, indent);
             lineJustify = renderer.getLineJustify(lineIndex, justify);
-            ranges = renderer.getRanges(lineOffset, line.length());
-            styles = renderer.getStyleRanges(lineOffset, line.length(), false);
-        }
-        if (styles is null) styles = new StyleRange[0];     
+            ranges = renderer.getRanges(lineOffset, line.length);
+            styles = renderer.getStyleRanges(lineOffset, line.length, false);
+        }
+        if (styles is null) styles = new StyleRange[0];
         Color lineBackground = renderer.getLineBackground(lineIndex, null);
         event = getLineBackgroundData(lineOffset, line);
         if (event !is null && event.lineBackground !is null) lineBackground = event.lineBackground;
@@ -895,11 +1000,11 @@
      *   <li>ERROR_IO when the writer is closed.</li>
      * </ul>
      */
-    public void writeLineDelimiter(String lineDelimiter) {
+    public override void writeLineDelimiter(String lineDelimiter) {
         if (isClosed()) {
             DWT.error(DWT.ERROR_IO);
         }
-        write(lineDelimiter, 0, lineDelimiter.length());
+        write(lineDelimiter, 0, lineDelimiter.length);
         write("\\par ");
     }
     /**
@@ -913,34 +1018,35 @@
      *
      * @param line line text to write as RTF. Must not contain line breaks
      *  Line breaks should be written using writeLineDelimiter()
-     * @param lineOffset offset of the line. 0 based from the start of the 
-     *  widget document. Any text occurring before the start offset or after the 
+     * @param lineOffset offset of the line. 0 based from the start of the
+     *  widget document. Any text occurring before the start offset or after the
      *  end offset specified during object creation is ignored.
      * @param styles styles to use for formatting. Must not be null.
-     * @param lineBackground line background color to use for formatting. 
+     * @param lineBackground line background color to use for formatting.
      *  May be null.
      */
     void writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles, Color lineBackground, int indent, int alignment, bool justify) {
-        int lineLength = line.length();
+        int lineLength = line.length;
         int startOffset = getStart();
         int writeOffset = startOffset - lineOffset;
         if (writeOffset >= lineLength) return;
         int lineIndex = Math.max(0, writeOffset);
-            
+
         write("\\fi");
         write(indent);
         switch (alignment) {
             case DWT.LEFT: write("\\ql"); break;
             case DWT.CENTER: write("\\qc"); break;
             case DWT.RIGHT: write("\\qr"); break;
+            default:
         }
         if (justify) write("\\qj");
         write(" ");
-        
+
         if (lineBackground !is null) {
             write("{\\highlight");
             write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
-            write(" "); 
+            write(" ");
         }
         int endOffset = startOffset + super.getCharCount();
         int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
@@ -965,7 +1071,7 @@
             // write any unstyled text
             if (lineIndex < start) {
                 // copy to start of style
-                // style starting beyond end of write range or end of line 
+                // style starting beyond end of write range or end of line
                 // is guarded against above.
                 write(line, lineIndex, start);
                 lineIndex = start;
@@ -988,10 +1094,10 @@
                 write(fontData.getHeight() * 2);
             } else {
                 if ((style.fontStyle & DWT.BOLD) !is 0) {
-                    write("\\b"); 
+                    write("\\b");
                 }
                 if ((style.fontStyle & DWT.ITALIC) !is 0) {
-                    write("\\i"); 
+                    write("\\i");
                 }
             }
             if (style.underline) {
@@ -1000,7 +1106,7 @@
             if (style.strikeout) {
                 write("\\strike");
             }
-            write(" "); 
+            write(" ");
             // copy to end of style or end of write range or end of line
             int copyEnd = Math.min(end, lineEndOffset);
             // guard against invalid styles and let style processing continue
@@ -1008,10 +1114,10 @@
             write(line, lineIndex, copyEnd);
             if (font is null) {
                 if ((style.fontStyle & DWT.BOLD) !is 0) {
-                    write("\\b0"); 
+                    write("\\b0");
                 }
                 if ((style.fontStyle & DWT.ITALIC) !is 0) {
-                    write("\\i0"); 
+                    write("\\i0");
                 }
             }
             if (style.underline) {
@@ -1032,21 +1138,21 @@
     }
     /**
      * The <code>TextWriter</code> class is used to write widget content to
-     * a String.  Whole and partial lines and line breaks can be written. To write 
-     * partial lines, specify the start and length of the desired segment 
+     * a string.  Whole and partial lines and line breaks can be written. To write
+     * partial lines, specify the start and length of the desired segment
      * during object creation.
      * <p>
-     * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid String only after close() 
+     * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
      * has been called.
      * </p>
      */
     class TextWriter {
         private StringBuffer buffer;
         private int startOffset;    // offset of first character that will be written
-        private int endOffset;      // offset of last character that will be written. 
-                                    // 0 based from the beginning of the widget text. 
-        private bool isClosed = false;
-    
+        private int endOffset;      // offset of last character that will be written.
+                                    // 0 based from the beginning of the widget text.
+        private bool isClosed_ = false;
+
     /**
      * Creates a writer that writes content starting at offset "start"
      * in the document.  <code>start</code> and <code>length</code> can be set to specify partial lines.
@@ -1061,23 +1167,23 @@
     }
     /**
      * Closes the writer. Once closed no more content can be written.
-     * <b>NOTE:</b>  <code>toString()</code> is not guaranteed to return a valid String unless
+     * <b>NOTE:</b>  <code>toString()</code> is not guaranteed to return a valid string unless
      * the writer is closed.
      */
     public void close() {
-        if (!isClosed) {
-            isClosed = true;
-        }
-    }
-    /** 
+        if (!isClosed_) {
+            isClosed_ = true;
+        }
+    }
+    /**
      * Returns the number of characters to write.
      * @return the integer number of characters to write
      */
     public int getCharCount() {
         return endOffset - startOffset;
-    }   
-    /** 
-     * Returns the offset where writing starts. 0 based from the start of 
+    }
+    /**
+     * Returns the offset where writing starts. 0 based from the start of
      * the widget text. Used to write partial lines.
      * @return the integer offset where writing starts
      */
@@ -1089,37 +1195,38 @@
      * @return a bool specifying whether or not the writer is closed
      */
     public bool isClosed() {
-        return isClosed;
+        return isClosed_;
     }
     /**
-     * Returns the String.  <code>close()</code> must be called before <code>toString()</code> 
-     * is guaranteed to return a valid String.
+     * Returns the string.  <code>close()</code> must be called before <code>toString()</code>
+     * is guaranteed to return a valid string.
      *
-     * @return the String
+     * @return the string
      */
-    public String toString() {
+    public override String toString() {
         return buffer.toString();
     }
     /**
-     * Appends the given String to the data.
+     * Appends the given string to the data.
      */
-    void write(String String) {
-        buffer.append(String);
+    void write(String string) {
+        buffer.append(string);
     }
     /**
-     * Inserts the given String to the data at the specified offset.
+     * Inserts the given string to the data at the specified offset.
      * <p>
      * Do nothing if "offset" is < 0 or > getCharCount()
      * </p>
      *
-     * @param String text to insert
-     * @param offset offset in the existing data to insert "String" at.
+     * @param string text to insert
+     * @param offset offset in the existing data to insert "string" at.
      */
-    void write(String String, int offset) {
+    void write(String string, int offset) {
         if (offset < 0 || offset > buffer.length()) {
             return;
         }
-        buffer.insert(offset, String);
+        buffer.select( offset );
+        buffer.prepend( string );
     }
     /**
      * Appends the given int to the data.
@@ -1138,19 +1245,19 @@
      *
      * @param line line text to write. Must not contain line breaks
      *  Line breaks should be written using writeLineDelimiter()
-     * @param lineOffset offset of the line. 0 based from the start of the 
-     *  widget document. Any text occurring before the start offset or after the 
+     * @param lineOffset offset of the line. 0 based from the start of the
+     *  widget document. Any text occurring before the start offset or after the
      *  end offset specified during object creation is ignored.
      * @exception DWTException <ul>
      *   <li>ERROR_IO when the writer is closed.</li>
      * </ul>
      */
-    public void writeLine(String line, int lineOffset) {    
-        if (isClosed) {
+    public void writeLine(String line, int lineOffset) {
+        if (isClosed_) {
             DWT.error(DWT.ERROR_IO);
-        }       
+        }
         int writeOffset = startOffset - lineOffset;
-        int lineLength = line.length();
+        int lineLength = line.length;
         int lineIndex;
         if (writeOffset >= lineLength) {
             return;                         // whole line is outside write range
@@ -1173,7 +1280,7 @@
      * </ul>
      */
     public void writeLineDelimiter(String lineDelimiter) {
-        if (isClosed) {
+        if (isClosed_) {
             DWT.error(DWT.ERROR_IO);
         }
         write(lineDelimiter);
@@ -1186,7 +1293,7 @@
  * <p>
  * The style value is either one of the style constants defined in
  * class <code>DWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together 
+ * class, or must be built by <em>bitwise OR</em>'ing together
  * (that is, using the <code>int</code> "|" operator) two or more
  * of those <code>DWT</code> style constants. The class description
  * lists the style constants that are applicable to the class.
@@ -1211,13 +1318,14 @@
  * @see #getStyle
  */
 public this(Composite parent, int style) {
+    selection = new Point(0, 0);
     super(parent, checkStyle(style));
     // set the fg in the OS to ensure that these are the same as StyledText, necessary
     // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
     super.setForeground(getForeground());
     super.setDragDetect(false);
     Display display = getDisplay();
-    isMirrored = (super.getStyle() & DWT.MIRRORED) !is 0;
+    isMirrored_ = (super.getStyle() & DWT.MIRRORED) !is 0;
     fixedLineHeight = true;
     if ((style & DWT.READ_ONLY) !is 0) {
         setEditable(false);
@@ -1240,7 +1348,7 @@
     }
     if (isBidiCaret()) {
         createCaretBitmaps();
-        Runnable runnable = new Runnable() {
+        Runnable runnable = new class() Runnable {
             public void run() {
                 int direction = BidiUtil.getKeyboardLanguage() is BidiUtil.KEYBOARD_BIDI ? DWT.RIGHT : DWT.LEFT;
                 if (direction is caretDirection) return;
@@ -1251,7 +1359,7 @@
         };
         BidiUtil.addLanguageListener(this, runnable);
     }
-    setCaret(defaultCaret); 
+    setCaret(defaultCaret);
     calculateScrollBars();
     createKeyBindings();
     setCursor(display.getSystemCursor(DWT.CURSOR_IBEAM));
@@ -1259,8 +1367,8 @@
     initializeAccessible();
     setData("DEFAULT_DROP_TARGET_EFFECT", new StyledTextDropTargetEffect(this));
 }
-/**  
- * Adds an extended modify listener. An ExtendedModify event is sent by the 
+/**
+ * Adds an extended modify listener. An ExtendedModify event is sent by the
  * widget when the widget text has changed.
  *
  * @param extendedModifyListener the listener
@@ -1281,15 +1389,15 @@
 /**
  * Adds a bidirectional segment listener.
  * <p>
- * A BidiSegmentEvent is sent 
- * whenever a line of text is measured or rendered. The user can 
- * specify text ranges in the line that should be treated as if they 
+ * A BidiSegmentEvent is sent
+ * whenever a line of text is measured or rendered. The user can
+ * specify text ranges in the line that should be treated as if they
  * had a different direction than the surrounding text.
  * This may be used when adjacent segments of right-to-left text should
- * not be reordered relative to each other. 
- * E.g., Multiple Java String literals in a right-to-left language
+ * not be reordered relative to each other.
+ * E.g., Multiple Java string literals in a right-to-left language
  * should generally remain in logical order to each other, that is, the
- * way they are stored. 
+ * way they are stored.
  * </p>
  *
  * @param listener the listener
@@ -1309,7 +1417,7 @@
     addListener(LineGetSegments, new StyledTextListener(listener));
 }
 /**
- * Adds a line background listener. A LineGetBackground event is sent by the 
+ * Adds a line background listener. A LineGetBackground event is sent by the
  * widget to determine the background color for a line.
  *
  * @param listener the listener
@@ -1330,7 +1438,7 @@
     addListener(LineGetBackground, new StyledTextListener(listener));
 }
 /**
- * Adds a line style listener. A LineGetStyle event is sent by the widget to 
+ * Adds a line style listener. A LineGetStyle event is sent by the widget to
  * determine the styles for a line.
  *
  * @param listener the listener
@@ -1351,8 +1459,8 @@
     }
     addListener(LineGetStyle, new StyledTextListener(listener));
 }
-/**  
- * Adds a modify listener. A Modify event is sent by the widget when the widget text 
+/**
+ * Adds a modify listener. A Modify event is sent by the widget when the widget text
  * has changed.
  *
  * @param modifyListener the listener
@@ -1369,7 +1477,7 @@
     if (modifyListener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     addListener(DWT.Modify, new TypedListener(modifyListener));
 }
-/**  
+/**
  * Adds a paint object listener. A paint object event is sent by the widget when an object
  * needs to be drawn.
  *
@@ -1381,9 +1489,9 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
  * </ul>
- * 
+ *
  * @since 3.2
- * 
+ *
  * @see PaintObjectListener
  * @see PaintObjectEvent
  */
@@ -1392,15 +1500,15 @@
     if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     addListener(PaintObject, new StyledTextListener(listener));
 }
-/**  
- * Adds a selection listener. A Selection event is sent by the widget when the 
+/**
+ * Adds a selection listener. A Selection event is sent by the widget when the
  * user changes the selection.
  * <p>
  * When <code>widgetSelected</code> is called, the event x and y fields contain
  * the start and end caret indices of the selection.
  * <code>widgetDefaultSelected</code> is not called for StyledTexts.
  * </p>
- * 
+ *
  * @param listener the listener which should be notified when the user changes the receiver's selection
 
  * @exception IllegalArgumentException <ul>
@@ -1420,10 +1528,10 @@
     if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     addListener(DWT.Selection, new TypedListener(listener));
 }
-/**  
- * Adds a verify key listener. A VerifyKey event is sent by the widget when a key 
- * is pressed. The widget ignores the key press if the listener sets the doit field 
- * of the event to false. 
+/**
+ * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
+ * is pressed. The widget ignores the key press if the listener sets the doit field
+ * of the event to false.
  *
  * @param listener the listener
  * @exception DWTException <ul>
@@ -1439,10 +1547,10 @@
     if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     addListener(VerifyKey, new StyledTextListener(listener));
 }
-/**  
- * Adds a verify listener. A Verify event is sent by the widget when the widget text 
- * is about to change. The listener can set the event text and the doit field to 
- * change the text that is set in the widget or to force the widget to ignore the 
+/**
+ * Adds a verify listener. A Verify event is sent by the widget when the widget text
+ * is about to change. The listener can set the event text and the doit field to
+ * change the text that is set in the widget or to force the widget to ignore the
  * text change.
  *
  * @param verifyListener the listener
@@ -1459,9 +1567,9 @@
     if (verifyListener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     addListener(DWT.Verify, new TypedListener(verifyListener));
 }
-/**  
- * Adds a word movement listener. A movement event is sent when the boundary 
- * of a word is needed. For example, this occurs during word next and word 
+/**
+ * Adds a word movement listener. A movement event is sent when the boundary
+ * of a word is needed. For example, this occurs during word next and word
  * previous actions.
  *
  * @param movementListener the listener
@@ -1472,12 +1580,12 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
  * </ul>
- * 
+ *
  * @see MovementEvent
  * @see MovementListener
  * @see #removeWordMovementListener
- * 
- * @since 3.3 
+ *
+ * @since 3.3
  */
 public void addWordMovementListener(MovementListener movementListener) {
     checkWidget();
@@ -1485,26 +1593,24 @@
     addListener(WordNext, new StyledTextListener(movementListener));
     addListener(WordPrevious, new StyledTextListener(movementListener));
 }
-/** 
- * Appends a String to the text at the end of the widget.
- *
- * @param String the String to be appended
+/**
+ * Appends a string to the text at the end of the widget.
+ *
+ * @param string the string to be appended
  * @see #replaceTextRange(int,int,String)
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * @exception IllegalArgumentException <ul>
- *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void append(String String) {
-    checkWidget();
-    if (String is null) {
-        DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    }
+ */
+public void append(String string) {
+    checkWidget();
+    // DWT extension: allow null for zero length string
+//     if (string is null) {
+//         DWT.error(DWT.ERROR_NULL_ARGUMENT);
+//     }
     int lastChar = Math.max(getCharCount(), 0);
-    replaceTextRange(lastChar, 0, String);
+    replaceTextRange(lastChar, 0, string);
 }
 /**
  * Calculates the scroll bars
@@ -1515,7 +1621,7 @@
     setScrollBars(true);
     if (verticalBar !is null) {
         verticalBar.setIncrement(getVerticalIncrement());
-    }   
+    }
     if (horizontalBar !is null) {
         horizontalBar.setIncrement(getHorizontalIncrement());
     }
@@ -1535,7 +1641,7 @@
             return;
         }
         topIndex = Compatibility.ceil(getVerticalScrollOffset(), verticalIncrement);
-        // Set top index to partially visible top line if no line is fully 
+        // Set top index to partially visible top line if no line is fully
         // visible but at least some of the widget client area is visible.
         // Fixes bug 15088.
         if (topIndex > 0) {
@@ -1543,7 +1649,7 @@
                 int bottomPixel = getVerticalScrollOffset() + clientAreaHeight;
                 int fullLineTopPixel = topIndex * verticalIncrement;
                 int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
-                // set top index to partially visible line if no line fully fits in 
+                // set top index to partially visible line if no line fully fits in
                 // client area or if space is available but not used (the latter should
                 // never happen because we use claimBottomFreeSpace)
                 if (fullLineVisibleHeight < verticalIncrement) {
@@ -1608,7 +1714,7 @@
     return style;
 }
 /**
- * Scrolls down the text to use new space made available by a resize or by 
+ * Scrolls down the text to use new space made available by a resize or by
  * deleted lines.
  */
 void claimBottomFreeSpace() {
@@ -1632,11 +1738,11 @@
  */
 void claimRightFreeSpace() {
     int newHorizontalOffset = Math.max(0, renderer.getWidth() - (clientAreaWidth - leftMargin - rightMargin));
-    if (newHorizontalOffset < horizontalScrollOffset) {         
+    if (newHorizontalOffset < horizontalScrollOffset) {
         // item is no longer drawn past the right border of the client area
-        // align the right end of the item with the right border of the 
+        // align the right end of the item with the right border of the
         // client area (window is scrolled right).
-        scrollHorizontal(newHorizontalOffset - horizontalScrollOffset, true);                   
+        scrollHorizontal(newHorizontalOffset - horizontalScrollOffset, true);
     }
 }
 /**
@@ -1646,7 +1752,7 @@
  */
 void clearSelection(bool sendEvent) {
     int selectionStart = selection.x;
-    int selectionEnd = selection.y; 
+    int selectionEnd = selection.y;
     resetSelection();
     // redraw old selection, if any
     if (selectionEnd - selectionStart > 0) {
@@ -1663,7 +1769,7 @@
         }
     }
 }
-public Point computeSize (int wHint, int hHint, bool changed) {
+public override Point computeSize (int wHint, int hHint, bool changed) {
     checkWidget();
     int lineCount = (getStyle() & DWT.SINGLE) !is 0 ? 1 : content.getLineCount();
     int width = 0;
@@ -1701,7 +1807,7 @@
  * <p>
  * The text will be put on the clipboard in plain text format and RTF format.
  * The <code>DND.CLIPBOARD</code> clipboard is used for data that is
- * transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) or 
+ * transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) or
  * by menu action.
  * </p>
  *
@@ -1715,24 +1821,24 @@
     copy(DND.CLIPBOARD);
 }
 /**
- * Copies the selected text to the specified clipboard.  The text will be put in the 
+ * Copies the selected text to the specified clipboard.  The text will be put in the
  * clipboard in plain text format and RTF format.
  * <p>
- * The clipboardType is  one of the clipboard constants defined in class 
- * <code>DND</code>.  The <code>DND.CLIPBOARD</code>  clipboard is 
- * used for data that is transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V) 
- * or by menu action.  The <code>DND.SELECTION_CLIPBOARD</code> 
- * clipboard is used for data that is transferred by selecting text and pasting 
+ * The clipboardType is  one of the clipboard constants defined in class
+ * <code>DND</code>.  The <code>DND.CLIPBOARD</code>  clipboard is
+ * used for data that is transferred by keyboard accelerator (such as Ctrl+C/Ctrl+V)
+ * or by menu action.  The <code>DND.SELECTION_CLIPBOARD</code>
+ * clipboard is used for data that is transferred by selecting text and pasting
  * with the middle mouse button.
  * </p>
- * 
+ *
  * @param clipboardType indicates the type of clipboard
  *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * 
+ *
  * @since 3.1
  */
 public void copy(int clipboardType) {
@@ -1743,7 +1849,7 @@
         try {
             setClipboardContent(selection.x, length, clipboardType);
         } catch (DWTError error) {
-            // Copy to clipboard failed. This happens when another application 
+            // Copy to clipboard failed. This happens when another application
             // is accessing the clipboard while we copy. Ignore the error.
             // Fixes 1GDQAVN
             // Rethrow all other errors. Fixes bug 17578.
@@ -1755,16 +1861,16 @@
 }
 /**
  * Returns the alignment of the widget.
- * 
+ *
  * @return the alignment
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *  
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @see #getLineAlignment(int)
- * 
+ *
  * @since 3.2
  */
 public int getAlignment() {
@@ -1802,24 +1908,24 @@
     return Math.min(height, availableHeight);
 }
 /**
- * Returns a String that uses only the line delimiter specified by the 
+ * Returns a string that uses only the line delimiter specified by the
  * StyledTextContent implementation.
  * <p>
  * Returns only the first line if the widget has the DWT.SINGLE style.
  * </p>
  *
- * @param text the text that may have line delimiters that don't 
- *  match the model line delimiter. Possible line delimiters 
+ * @param text the text that may have line delimiters that don't
+ *  match the model line delimiter. Possible line delimiters
  *  are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
- * @return the converted text that only uses the line delimiter 
- *  specified by the model. Returns only the first line if the widget 
+ * @return the converted text that only uses the line delimiter
+ *  specified by the model. Returns only the first line if the widget
  *  has the DWT.SINGLE style.
  */
-String getModelDelimitedText(String text) { 
-    int length = text.length();
+String getModelDelimitedText(String text) {
+    int length = text.length;
     if (length is 0) {
         return text;
-    }   
+    }
     int crIndex = 0;
     int lfIndex = 0;
     int i = 0;
@@ -1827,10 +1933,10 @@
     String delimiter = getLineDelimiter();
     while (i < length) {
         if (crIndex !is -1) {
-            crIndex = text.indexOf(DWT.CR, i);
+            crIndex = text.indexOf (DWT.CR, i);
         }
         if (lfIndex !is -1) {
-            lfIndex = text.indexOf(DWT.LF, i);
+            lfIndex = text.indexOf (DWT.LF, i);
         }
         if (lfIndex is -1 && crIndex is -1) {   // no more line breaks?
             break;
@@ -1850,7 +1956,7 @@
         }
         convertedText.append(delimiter);
     }
-    // copy remaining text if any and if not in single line mode or no 
+    // copy remaining text if any and if not in single line mode or no
     // text copied thus far (because there only is one line)
     if (i < length && (!isSingleLine() || convertedText.length() is 0)) {
         convertedText.append(text.substring(i));
@@ -1877,9 +1983,9 @@
 void createKeyBindings() {
     int nextKey = isMirrored() ? DWT.ARROW_LEFT : DWT.ARROW_RIGHT;
     int previousKey = isMirrored() ? DWT.ARROW_RIGHT : DWT.ARROW_LEFT;
-    
+
     // Navigation
-    setKeyBinding(DWT.ARROW_UP, ST.LINE_UP);    
+    setKeyBinding(DWT.ARROW_UP, ST.LINE_UP);
     setKeyBinding(DWT.ARROW_DOWN, ST.LINE_DOWN);
     if (IS_CARBON) {
         setKeyBinding(previousKey | DWT.MOD1, ST.LINE_START);
@@ -1904,23 +2010,23 @@
     setKeyBinding(DWT.PAGE_DOWN | DWT.MOD1, ST.WINDOW_END);
     setKeyBinding(nextKey, ST.COLUMN_NEXT);
     setKeyBinding(previousKey, ST.COLUMN_PREVIOUS);
-    
+
     // Selection
-    setKeyBinding(DWT.ARROW_UP | DWT.MOD2, ST.SELECT_LINE_UP);  
+    setKeyBinding(DWT.ARROW_UP | DWT.MOD2, ST.SELECT_LINE_UP);
     setKeyBinding(DWT.ARROW_DOWN | DWT.MOD2, ST.SELECT_LINE_DOWN);
     if (IS_CARBON) {
         setKeyBinding(previousKey | DWT.MOD1 | DWT.MOD2, ST.SELECT_LINE_START);
         setKeyBinding(nextKey | DWT.MOD1 | DWT.MOD2, ST.SELECT_LINE_END);
-        setKeyBinding(DWT.HOME | DWT.MOD2, ST.SELECT_TEXT_START);   
+        setKeyBinding(DWT.HOME | DWT.MOD2, ST.SELECT_TEXT_START);
         setKeyBinding(DWT.END | DWT.MOD2, ST.SELECT_TEXT_END);
-        setKeyBinding(DWT.ARROW_UP | DWT.MOD1 | DWT.MOD2, ST.SELECT_TEXT_START);    
+        setKeyBinding(DWT.ARROW_UP | DWT.MOD1 | DWT.MOD2, ST.SELECT_TEXT_START);
         setKeyBinding(DWT.ARROW_DOWN | DWT.MOD1 | DWT.MOD2, ST.SELECT_TEXT_END);
         setKeyBinding(nextKey | DWT.MOD2 | DWT.MOD3, ST.SELECT_WORD_NEXT);
         setKeyBinding(previousKey | DWT.MOD2 | DWT.MOD3, ST.SELECT_WORD_PREVIOUS);
     } else  {
         setKeyBinding(DWT.HOME | DWT.MOD2, ST.SELECT_LINE_START);
         setKeyBinding(DWT.END | DWT.MOD2, ST.SELECT_LINE_END);
-        setKeyBinding(DWT.HOME | DWT.MOD1 | DWT.MOD2, ST.SELECT_TEXT_START);    
+        setKeyBinding(DWT.HOME | DWT.MOD1 | DWT.MOD2, ST.SELECT_TEXT_START);
         setKeyBinding(DWT.END | DWT.MOD1 | DWT.MOD2, ST.SELECT_TEXT_END);
         setKeyBinding(nextKey | DWT.MOD1 | DWT.MOD2, ST.SELECT_WORD_NEXT);
         setKeyBinding(previousKey | DWT.MOD1 | DWT.MOD2, ST.SELECT_WORD_PREVIOUS);
@@ -1930,8 +2036,8 @@
     setKeyBinding(DWT.PAGE_UP | DWT.MOD1 | DWT.MOD2, ST.SELECT_WINDOW_START);
     setKeyBinding(DWT.PAGE_DOWN | DWT.MOD1 | DWT.MOD2, ST.SELECT_WINDOW_END);
     setKeyBinding(nextKey | DWT.MOD2, ST.SELECT_COLUMN_NEXT);
-    setKeyBinding(previousKey | DWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);   
-                
+    setKeyBinding(previousKey | DWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
+
     // Modification
     // Cut, Copy, Paste
     setKeyBinding('X' | DWT.MOD1, ST.CUT);
@@ -1952,7 +2058,7 @@
     setKeyBinding(DWT.DEL, ST.DELETE_NEXT);
     setKeyBinding(DWT.BS | DWT.MOD1, ST.DELETE_WORD_PREVIOUS);
     setKeyBinding(DWT.DEL | DWT.MOD1, ST.DELETE_WORD_NEXT);
-    
+
     // Miscellaneous
     setKeyBinding(DWT.INSERT, ST.TOGGLE_OVERWRITE);
 }
@@ -1965,30 +2071,30 @@
     int caretWidth = BIDI_CARET_WIDTH;
     Display display = getDisplay();
     if (leftCaretBitmap !is null) {
-        if (defaultCaret !is null && leftCaretBitmap.opEquals(defaultCaret.getImage())) {
+        if (defaultCaret !is null && leftCaretBitmap==/*eq*/defaultCaret.getImage()) {
             defaultCaret.setImage(null);
         }
         leftCaretBitmap.dispose();
     }
     int lineHeight = renderer.getLineHeight();
     leftCaretBitmap = new Image(display, caretWidth, lineHeight);
-    GC gc = new GC (leftCaretBitmap); 
+    GC gc = new GC (leftCaretBitmap);
     gc.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
     gc.fillRectangle(0, 0, caretWidth, lineHeight);
     gc.setForeground(display.getSystemColor(DWT.COLOR_WHITE));
     gc.drawLine(0,0,0,lineHeight);
     gc.drawLine(0,0,caretWidth-1,0);
     gc.drawLine(0,1,1,1);
-    gc.dispose();   
-    
+    gc.dispose();
+
     if (rightCaretBitmap !is null) {
-        if (defaultCaret !is null && rightCaretBitmap.opEquals(defaultCaret.getImage())) {
+        if (defaultCaret !is null && rightCaretBitmap==/*eq*/defaultCaret.getImage()) {
             defaultCaret.setImage(null);
         }
         rightCaretBitmap.dispose();
     }
     rightCaretBitmap = new Image(display, caretWidth, lineHeight);
-    gc = new GC (rightCaretBitmap); 
+    gc = new GC (rightCaretBitmap);
     gc.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
     gc.fillRectangle(0, 0, caretWidth, lineHeight);
     gc.setForeground(display.getSystemColor(DWT.COLOR_WHITE));
@@ -1998,7 +2104,7 @@
     gc.dispose();
 }
 /**
- * Moves the selected text to the clipboard.  The text will be put in the 
+ * Moves the selected text to the clipboard.  The text will be put in the
  * clipboard in plain text format and RTF format.
  *
  * @exception DWTException <ul>
@@ -2013,7 +2119,7 @@
         try {
             setClipboardContent(selection.x, length, DND.CLIPBOARD);
         } catch (DWTError error) {
-            // Copy to clipboard failed. This happens when another application 
+            // Copy to clipboard failed. This happens when another application
             // is accessing the clipboard while we copy. Ignore the error.
             // Fixes 1GDQAVN
             // Rethrow all other errors. Fixes bug 17578.
@@ -2027,9 +2133,9 @@
         doDelete();
     }
 }
-/** 
+/**
  * A mouse move event has occurred.  See if we should start autoscrolling.  If
- * the move position is outside of the client area, initiate autoscrolling.  
+ * the move position is outside of the client area, initiate autoscrolling.
  * Otherwise, we've moved back into the widget so end autoscrolling.
  */
 void doAutoScroll(Event event) {
@@ -2045,7 +2151,7 @@
         endAutoScroll();
     }
 }
-/** 
+/**
  * Initiates autoscrolling.
  *
  * @param direction DWT.UP, DWT.DOWN, DWT.COLUMN_NEXT, DWT.COLUMN_PREVIOUS
@@ -2056,13 +2162,15 @@
     if (autoScrollDirection is direction) {
         return;
     }
-    
+
     Runnable timer = null;
-    final Display display = getDisplay();
+    final Display disp = getDisplay();
     // Set a timer that will simulate the user pressing and holding
     // down a cursor key (i.e., arrowUp, arrowDown).
     if (direction is DWT.UP) {
-        timer = new Runnable() {
+        timer = new class(disp) Runnable {
+            Display display;
+            this( Display d ){ this.display = d; }
             public void run() {
                 if (autoScrollDirection is DWT.UP) {
                     doSelectionPageUp(autoScrollDistance);
@@ -2073,7 +2181,9 @@
         autoScrollDirection = direction;
         display.timerExec(V_SCROLL_RATE, timer);
     } else if (direction is DWT.DOWN) {
-        timer = new Runnable() {
+        timer = new class(disp) Runnable {
+            Display display;
+            this( Display d ){ this.display = d; }
             public void run() {
                 if (autoScrollDirection is DWT.DOWN) {
                     doSelectionPageDown(autoScrollDistance);
@@ -2084,7 +2194,9 @@
         autoScrollDirection = direction;
         display.timerExec(V_SCROLL_RATE, timer);
     } else if (direction is ST.COLUMN_NEXT) {
-        timer = new Runnable() {
+        timer = new class(disp) Runnable {
+            Display display;
+            this( Display d ){ this.display = d; }
             public void run() {
                 if (autoScrollDirection is ST.COLUMN_NEXT) {
                     doVisualNext();
@@ -2097,7 +2209,9 @@
         autoScrollDirection = direction;
         display.timerExec(H_SCROLL_RATE, timer);
     } else if (direction is ST.COLUMN_PREVIOUS) {
-        timer = new Runnable() {
+        timer = new class(disp) Runnable {
+            Display display;
+            this( Display d ){ this.display = d; }
             public void run() {
                 if (autoScrollDirection is ST.COLUMN_PREVIOUS) {
                     doVisualPrevious();
@@ -2126,13 +2240,14 @@
         int lineIndex = content.getLineAtOffset(caretOffset);
         int lineOffset = content.getOffsetAtLine(lineIndex);
         if (caretOffset is lineOffset) {
+            // DWT: on line start, delete line break
             lineOffset = content.getOffsetAtLine(lineIndex - 1);
-            event.start = lineOffset + content.getLine(lineIndex - 1).length();
+            event.start = lineOffset + content.getLine(lineIndex - 1).length;
             event.end = caretOffset;
         } else {
             TextLayout layout = renderer.getTextLayout(lineIndex);
-            int start = layout.getPreviousOffset(caretOffset - lineOffset, DWT.MOVEMENT_CHAR);
-            renderer.disposeTextLayout(layout); 
+            int start = layout.getPreviousOffset(caretOffset - lineOffset, DWT.MOVEMENT_CLUSTER);
+            renderer.disposeTextLayout(layout);
             event.start = start + lineOffset;
             event.end = caretOffset;
         }
@@ -2140,16 +2255,16 @@
     }
 }
 /**
- * Replaces the selection with the character or insert the character at the 
+ * Replaces the selection with the character or insert the character at the
  * current caret position if no selection exists.
  * <p>
- * If a carriage return was typed replace it with the line break character 
+ * If a carriage return was typed replace it with the line break character
  * used by the widget on this platform.
  * </p>
  *
  * @param key the character typed by the user
  */
-void doContent(char key) {
+void doContent(dchar key) {
     Event event = new Event();
     event.start = selection.x;
     event.end = selection.y;
@@ -2162,18 +2277,18 @@
         }
     } else if (selection.x is selection.y && overwrite && key !is TAB) {
         // no selection and overwrite mode is on and the typed key is not a
-        // tab character (tabs are always inserted without overwriting)?    
+        // tab character (tabs are always inserted without overwriting)?
         int lineIndex = content.getLineAtOffset(event.end);
         int lineOffset = content.getOffsetAtLine(lineIndex);
         String line = content.getLine(lineIndex);
-        // replace character at caret offset if the caret is not at the 
+        // replace character at caret offset if the caret is not at the
         // end of the line
-        if (event.end < lineOffset + line.length()) {
-            event.end++;
-        }
-        event.text = new String(new char[] {key});
+        if (event.end < lineOffset + line.length) {
+            event.end+=dcharToString( key ).length;
+        }
+        event.text = dcharToString( key );
     } else {
-        event.text = new String(new char[] {key});
+        event.text = dcharToString( key );
     }
     if (event.text !is null) {
         if (textLimit > 0 && content.getCharCount() - (event.end - event.start) >= textLimit) {
@@ -2186,12 +2301,12 @@
  * Moves the caret after the last character of the widget content.
  */
 void doContentEnd() {
-    // place caret at end of first line if receiver is in single 
+    // place caret at end of first line if receiver is in single
     // line mode. fixes 4820.
     if (isSingleLine()) {
         doLineEnd();
     } else {
-        int length = content.getCharCount();        
+        int length = content.getCharCount();
         if (caretOffset < length) {
             caretOffset = length;
             showCaret();
@@ -2209,7 +2324,7 @@
 }
 /**
  * Moves the caret to the start of the selection if a selection exists.
- * Otherwise, if no selection exists move the cursor according to the 
+ * Otherwise, if no selection exists move the cursor according to the
  * cursor selection rules.
  *
  * @see #doSelectionCursorPrevious
@@ -2225,7 +2340,7 @@
 }
 /**
  * Moves the caret to the end of the selection if a selection exists.
- * Otherwise, if no selection exists move the cursor according to the 
+ * Otherwise, if no selection exists move the cursor according to the
  * cursor selection rules.
  *
  * @see #doSelectionCursorNext
@@ -2252,7 +2367,7 @@
     } else if (caretOffset < content.getCharCount()) {
         int line = content.getLineAtOffset(caretOffset);
         int lineOffset = content.getOffsetAtLine(line);
-        int lineLength = content.getLine(line).length();
+        int lineLength = content.getLine(line).length;
         if (caretOffset is lineOffset + lineLength) {
             event.start = caretOffset;
             event.end = content.getOffsetAtLine(line + 1);
@@ -2268,7 +2383,7 @@
  */
 void doDeleteWordNext() {
     if (selection.x !is selection.y) {
-        // if a selection exists, treat the as if 
+        // if a selection exists, treat the as if
         // only the delete key was pressed
         doDelete();
     } else {
@@ -2284,7 +2399,7 @@
  */
 void doDeleteWordPrevious() {
     if (selection.x !is selection.y) {
-        // if a selection exists, treat as if 
+        // if a selection exists, treat as if
         // only the backspace key was pressed
         doBackspace();
     } else {
@@ -2296,8 +2411,8 @@
     }
 }
 /**
- * Moves the caret one line down and to the same character offset relative 
- * to the beginning of the line. Move the caret to the end of the new line 
+ * Moves the caret one line down and to the same character offset relative
+ * to the beginning of the line. Move the caret to the end of the new line
  * if the new line is shorter than the character offset.
  */
 void doLineDown(bool select) {
@@ -2330,8 +2445,8 @@
     int oldColumnX = columnX;
     int oldHScrollOffset = horizontalScrollOffset;
     if (select) {
-        setMouseWordSelectionAnchor();  
-        // select first and then scroll to reduce flash when key 
+        setMouseWordSelectionAnchor();
+        // select first and then scroll to reduce flash when key
         // repeat scrolls lots of lines
         doSelection(ST.COLUMN_NEXT);
     }
@@ -2344,7 +2459,7 @@
  */
 void doLineEnd() {
     int caretLine = getCaretLine();
-    int lineOffset = content.getOffsetAtLine(caretLine);    
+    int lineOffset = content.getOffsetAtLine(caretLine);
     int lineEndOffset;
     if (wordWrap) {
         TextLayout layout = renderer.getTextLayout(caretLine);
@@ -2354,7 +2469,7 @@
         lineEndOffset = lineOffset + offsets[lineIndex + 1];
         renderer.disposeTextLayout(layout);
     } else {
-        int lineLength = content.getLine(caretLine).length();
+        int lineLength = content.getLine(caretLine).length;
         lineEndOffset = lineOffset + lineLength;
     }
     if (caretOffset < lineEndOffset) {
@@ -2384,8 +2499,8 @@
     }
 }
 /**
- * Moves the caret one line up and to the same character offset relative 
- * to the beginning of the line. Move the caret to the end of the new line 
+ * Moves the caret one line up and to the same character offset relative
+ * to the beginning of the line. Move the caret to the end of the new line
  * if the new line is shorter than the character offset.
  */
 void doLineUp(bool select) {
@@ -2435,25 +2550,25 @@
     int line = getLineIndex(y);
 
     updateCaretDirection = true;
-    // allow caret to be placed below first line only if receiver is 
+    // allow caret to be placed below first line only if receiver is
     // not in single line mode. fixes 4820.
     if (line < 0 || (isSingleLine() && line > 0)) {
         return;
     }
     int oldCaretAlignment = caretAlignment;
     int newCaretOffset = getOffsetAtPoint(x, y);
-    
+
     if (doubleClickEnabled && clickCount > 1) {
         newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
     }
-    
+
     int newCaretLine = content.getLineAtOffset(newCaretOffset);
-    
-    // Is the mouse within the left client area border or on 
-    // a different line? If not the autoscroll selection 
+
+    // Is the mouse within the left client area border or on
+    // a different line? If not the autoscroll selection
     // could be incorrectly reset. Fixes 1GKM3XS
-    if (0 <= y && y < clientAreaHeight && 
-        (0 <= x && x < clientAreaWidth || wordWrap ||   
+    if (0 <= y && y < clientAreaHeight &&
+        (0 <= x && x < clientAreaWidth || wordWrap ||
         newCaretLine !is content.getLineAtOffset(caretOffset))) {
         if (newCaretOffset !is caretOffset || caretAlignment !is oldCaretAlignment) {
             caretOffset = newCaretOffset;
@@ -2470,8 +2585,8 @@
  * Updates the selection based on the caret position
  */
 void doMouseSelection() {
-    if (caretOffset <= selection.x || 
-        (caretOffset > selection.x && 
+    if (caretOffset <= selection.x ||
+        (caretOffset > selection.x &&
          caretOffset < selection.y && selectionAnchor is selection.x)) {
         doSelection(ST.COLUMN_PREVIOUS);
     } else {
@@ -2479,20 +2594,20 @@
     }
 }
 /**
- * Returns the offset of the word at the specified offset. 
- * If the current selection : from high index to low index 
- * (i.e., right to left, or caret is at left border of selection on 
+ * Returns the offset of the word at the specified offset.
+ * If the current selection extends from high index to low index
+ * (i.e., right to left, or caret is at left border of selection on
  * non-bidi platforms) the start offset of the word preceding the
- * selection is returned. If the current selection : from 
- * low index to high index the end offset of the word following 
+ * selection is returned. If the current selection extends from
+ * low index to high index the end offset of the word following
  * the selection is returned.
- * 
+ *
  * @param x mouse x location
  * @param newCaretOffset caret offset of the mouse cursor location
  * @param line line index of the mouse cursor location
  */
 int doMouseWordSelect(int x, int newCaretOffset, int line) {
-    // flip selection anchor based on word selection direction from 
+    // flip selection anchor based on word selection direction from
     // base double click. Always do this here (and don't rely on doAutoScroll)
     // because auto scroll only does not cover all possible mouse selections
     // (e.g., mouse x < 0 && mouse y > caret line y)
@@ -2517,7 +2632,7 @@
                 if (line + 1 < content.getLineCount()) {
                     lineEnd = content.getOffsetAtLine(line + 1);
                 }
-                newCaretOffset = lineEnd; 
+                newCaretOffset = lineEnd;
             }
         }
     }
@@ -2527,9 +2642,9 @@
  * Scrolls one page down so that the last line (truncated or whole)
  * of the current page becomes the fully visible top line.
  * <p>
- * The caret is scrolled the same number of lines so that its location 
- * relative to the top line remains the same. The exception is the end 
- * of the text where a full page scroll is not possible. In this case 
+ * The caret is scrolled the same number of lines so that its location
+ * relative to the top line remains the same. The exception is the end
+ * of the text where a full page scroll is not possible. In this case
  * the caret is moved after the last character.
  * </p>
  *
@@ -2546,7 +2661,7 @@
             int lineHeight = renderer.getLineHeight();
             int lines = (height is -1 ? clientAreaHeight : height) / lineHeight;
             int scrollLines = Math.min(lineCount - caretLine - 1, lines);
-            // ensure that scrollLines never gets negative and at least one 
+            // ensure that scrollLines never gets negative and at least one
             // line is scrolled. fixes bug 5602.
             scrollLines = Math.max(1, scrollLines);
             caretOffset = getOffsetAtPoint(columnX, getLinePixel(caretLine + scrollLines));
@@ -2627,10 +2742,10 @@
         height = getAvailableHeightBellow(height);
         scrollVertical(height, true);
         if (height is 0) setCaretLocation();
-    }   
+    }
     showCaret();
     int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
-    columnX = oldColumnX + hScrollChange;   
+    columnX = oldColumnX + hScrollChange;
 }
 /**
  * Moves the cursor to the end of the last fully visible line.
@@ -2652,14 +2767,14 @@
                 index--;
             }
             if (index is -1 && lineIndex > 0) {
-                bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length();
+                bottomOffset = content.getOffsetAtLine(lineIndex - 1) + content.getLine(lineIndex - 1).length;
             } else {
                 bottomOffset = content.getOffsetAtLine(lineIndex) + Math.max(0, layout.getLineOffsets()[index + 1] - 1);
             }
             renderer.disposeTextLayout(layout);
         } else {
             int lineIndex = getBottomIndex();
-            bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length();
+            bottomOffset = content.getOffsetAtLine(lineIndex) + content.getLine(lineIndex).length;
         }
         if (caretOffset < bottomOffset) {
             caretOffset = bottomOffset;
@@ -2695,7 +2810,7 @@
         } else {
             topOffset = content.getOffsetAtLine(lineIndex) + layout.getLineOffsets()[index];
         }
-        renderer.disposeTextLayout(layout);     
+        renderer.disposeTextLayout(layout);
     } else {
         topOffset = content.getOffsetAtLine(topIndex);
     }
@@ -2708,17 +2823,17 @@
 /**
  * Scrolls one page up so that the first line (truncated or whole)
  * of the current page becomes the fully visible last line.
- * The caret is scrolled the same number of lines so that its location 
- * relative to the top line remains the same. The exception is the beginning 
+ * The caret is scrolled the same number of lines so that its location
+ * relative to the top line remains the same. The exception is the beginning
  * of the text where a full page scroll is not possible. In this case the
  * caret is moved in front of the first character.
  */
 void doPageUp(bool select, int height) {
     if (isSingleLine()) return;
     int oldHScrollOffset = horizontalScrollOffset;
-    int oldColumnX = columnX;   
+    int oldColumnX = columnX;
     if (isFixedLineHeight()) {
-        int caretLine = getCaretLine(); 
+        int caretLine = getCaretLine();
         if (caretLine > 0) {
             int lineHeight = renderer.getLineHeight();
             int lines = (height is -1 ? clientAreaHeight : height) / lineHeight;
@@ -2798,11 +2913,11 @@
             lineHeight = renderer.getLineHeight(--lineIndex);
         }
         lineHeight = renderer.getLineHeight(lineIndex);
-        caretOffset = getOffsetAtPoint(columnX, lineHeight - caretHeight, lineIndex);   
+        caretOffset = getOffsetAtPoint(columnX, lineHeight - caretHeight, lineIndex);
         if (select) doSelection(ST.COLUMN_PREVIOUS);
         height = getAvailableHeightAbove(height);
         scrollVertical(-height, true);
-        if (height is 0) setCaretLocation();    
+        if (height is 0) setCaretLocation();
     }
     showCaret();
     int hScrollChange = oldHScrollOffset - horizontalScrollOffset;
@@ -2813,15 +2928,15 @@
  */
 void doSelection(int direction) {
     int redrawStart = -1;
-    int redrawEnd = -1; 
+    int redrawEnd = -1;
     if (selectionAnchor is -1) {
         selectionAnchor = selection.x;
-    }   
+    }
     if (direction is ST.COLUMN_PREVIOUS) {
         if (caretOffset < selection.x) {
             // grow selection
-            redrawEnd = selection.x; 
-            redrawStart = selection.x = caretOffset;        
+            redrawEnd = selection.x;
+            redrawStart = selection.x = caretOffset;
             // check if selection has reversed direction
             if (selection.y !is selectionAnchor) {
                 redrawEnd = selection.y;
@@ -2829,10 +2944,10 @@
             }
         // test whether selection actually changed. Fixes 1G71EO1
         } else if (selectionAnchor is selection.x && caretOffset < selection.y) {
-            // caret moved towards selection anchor (left side of selection). 
-            // shrink selection         
+            // caret moved towards selection anchor (left side of selection).
+            // shrink selection
             redrawEnd = selection.y;
-            redrawStart = selection.y = caretOffset;        
+            redrawStart = selection.y = caretOffset;
         }
     } else {
         if (caretOffset > selection.y) {
@@ -2841,15 +2956,15 @@
             redrawEnd = selection.y = caretOffset;
             // check if selection has reversed direction
             if (selection.x !is selectionAnchor) {
-                redrawStart = selection.x;              
+                redrawStart = selection.x;
                 selection.x = selectionAnchor;
             }
-        // test whether selection actually changed. Fixes 1G71EO1   
+        // test whether selection actually changed. Fixes 1G71EO1
         } else if (selectionAnchor is selection.y && caretOffset > selection.x) {
-            // caret moved towards selection anchor (right side of selection). 
-            // shrink selection         
+            // caret moved towards selection anchor (right side of selection).
+            // shrink selection
             redrawStart = selection.x;
-            redrawEnd = selection.x = caretOffset;      
+            redrawEnd = selection.x = caretOffset;
         }
     }
     if (redrawStart !is -1 && redrawEnd !is -1) {
@@ -2858,14 +2973,14 @@
     }
 }
 /**
- * Moves the caret to the next character or to the beginning of the 
+ * Moves the caret to the next character or to the beginning of the
  * next line if the cursor is at the end of a line.
  */
 void doSelectionCursorNext() {
     int caretLine = getCaretLine();
     int lineOffset = content.getOffsetAtLine(caretLine);
     int offsetInLine = caretOffset - lineOffset;
-    if (offsetInLine < content.getLine(caretLine).length()) {
+    if (offsetInLine < content.getLine(caretLine).length) {
         TextLayout layout = renderer.getTextLayout(caretLine);
         offsetInLine = layout.getNextOffset(offsetInLine, DWT.MOVEMENT_CLUSTER);
         int lineStart = layout.getLineOffsets()[layout.getLineIndex(offsetInLine)];
@@ -2874,14 +2989,14 @@
         caretAlignment = offsetInLine is lineStart ? OFFSET_LEADING : PREVIOUS_OFFSET_TRAILING;
         showCaret();
     } else if (caretLine < content.getLineCount() - 1 && !isSingleLine()) {
-        caretLine++;        
+        caretLine++;
         caretOffset = content.getOffsetAtLine(caretLine);
         caretAlignment = PREVIOUS_OFFSET_TRAILING;
         showCaret();
     }
 }
 /**
- * Moves the caret to the previous character or to the end of the previous 
+ * Moves the caret to the previous character or to the end of the previous
  * line if the cursor is at the beginning of a line.
  */
 void doSelectionCursorPrevious() {
@@ -2889,21 +3004,22 @@
     int lineOffset = content.getOffsetAtLine(caretLine);
     int offsetInLine = caretOffset - lineOffset;
     caretAlignment = OFFSET_LEADING;
+
     if (offsetInLine > 0) {
         caretOffset = getClusterPrevious(caretOffset, caretLine);
         showCaret();
     } else if (caretLine > 0) {
         caretLine--;
         lineOffset = content.getOffsetAtLine(caretLine);
-        caretOffset = lineOffset + content.getLine(caretLine).length();
+        caretOffset = lineOffset + content.getLine(caretLine).length;
         showCaret();
     }
 }
 /**
- * Moves the caret one line down and to the same character offset relative 
- * to the beginning of the line. Moves the caret to the end of the new line 
+ * Moves the caret one line down and to the same character offset relative
+ * to the beginning of the line. Moves the caret to the end of the new line
  * if the new line is shorter than the character offset.
- * Moves the caret to the end of the text if the caret already is on the 
+ * Moves the caret to the end of the text if the caret already is on the
  * last line.
  * Adjusts the selection according to the caret change. This can either add
  * to or subtract from the old selection, depending on the previous selection
@@ -2915,8 +3031,8 @@
     columnX = oldColumnX;
 }
 /**
- * Moves the caret one line up and to the same character offset relative 
- * to the beginning of the line. Moves the caret to the end of the new line 
+ * Moves the caret one line up and to the same character offset relative
+ * to the beginning of the line. Moves the caret to the end of the new line
  * if the new line is shorter than the character offset.
  * Moves the caret to the beginning of the document if it is already on the
  * first line.
@@ -2925,17 +3041,17 @@
  * direction.
  */
 void doSelectionLineUp() {
-    int oldColumnX = columnX = getPointAtOffset(caretOffset).x; 
-    doLineUp(true); 
+    int oldColumnX = columnX = getPointAtOffset(caretOffset).x;
+    doLineUp(true);
     columnX = oldColumnX;
 }
 /**
  * Scrolls one page down so that the last line (truncated or whole)
  * of the current page becomes the fully visible top line.
  * <p>
- * The caret is scrolled the same number of lines so that its location 
- * relative to the top line remains the same. The exception is the end 
- * of the text where a full page scroll is not possible. In this case 
+ * The caret is scrolled the same number of lines so that its location
+ * relative to the top line remains the same. The exception is the end
+ * of the text where a full page scroll is not possible. In this case
  * the caret is moved after the last character.
  * <p></p>
  * Adjusts the selection according to the caret change. This can either add
@@ -2952,8 +3068,8 @@
  * Scrolls one page up so that the first line (truncated or whole)
  * of the current page becomes the fully visible last line.
  * <p>
- * The caret is scrolled the same number of lines so that its location 
- * relative to the top line remains the same. The exception is the beginning 
+ * The caret is scrolled the same number of lines so that its location
+ * relative to the top line remains the same. The exception is the beginning
  * of the text where a full page scroll is not possible. In this case the
  * caret is moved in front of the first character.
  * </p><p>
@@ -2974,9 +3090,9 @@
     int newCaretOffset = getWordNext(caretOffset, DWT.MOVEMENT_WORD);
     // Force symmetrical movement for word next and previous. Fixes 14536
     caretAlignment = OFFSET_LEADING;
-    // don't change caret position if in single line mode and the cursor 
+    // don't change caret position if in single line mode and the cursor
     // would be on a different line. fixes 5673
-    if (!isSingleLine() || 
+    if (!isSingleLine() ||
         content.getLineAtOffset(caretOffset) is content.getLineAtOffset(newCaretOffset)) {
         caretOffset = newCaretOffset;
         showCaret();
@@ -2999,8 +3115,8 @@
 }
 /**
  * Moves the caret one character to the left.  Do not go to the previous line.
- * When in a bidi locale and at a R2L character the caret is moved to the 
- * beginning of the R2L segment (visually right) and then one character to the 
+ * When in a bidi locale and at a R2L character the caret is moved to the
+ * beginning of the R2L segment (visually right) and then one character to the
  * left (visually left because it's now in a L2R segment).
  */
 void doVisualPrevious() {
@@ -3009,8 +3125,8 @@
 }
 /**
  * Moves the caret one character to the right.  Do not go to the next line.
- * When in a bidi locale and at a R2L character the caret is moved to the 
- * end of the R2L segment (visually left) and then one character to the 
+ * When in a bidi locale and at a R2L character the caret is moved to the
+ * end of the R2L segment (visually left) and then one character to the
  * right (visually right because it's now in a L2R segment).
  */
 void doVisualNext() {
@@ -3043,13 +3159,13 @@
         doSelectionWordPrevious();
     }
 }
-/** 
+/**
  * Ends the autoscroll process.
  */
 void endAutoScroll() {
     autoScrollDirection = DWT.NULL;
 }
-public Color getBackground() {
+public override Color getBackground() {
     checkWidget();
     if (background is null) {
         return getDisplay().getSystemColor(DWT.COLOR_LIST_BACKGROUND);
@@ -3058,9 +3174,9 @@
 }
 /**
  * Returns the baseline, in pixels.
- *  
- * Note: this API should not be used if a StyleRange attribute causes lines to 
- * have different heights (i.e. different fonts, rise, etc). 
+ *
+ * Note: this API should not be used if a StyleRange attribute causes lines to
+ * have different heights (i.e. different fonts, rise, etc).
  *
  * @return baseline the baseline
  * @exception DWTException <ul>
@@ -3068,7 +3184,7 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @since 3.0
- * 
+ *
  * @see #getBaseline(int)
  */
 public int getBaseline() {
@@ -3076,20 +3192,20 @@
     return renderer.getBaseline();
 }
 /**
- * Returns the baseline at the given offset, in pixels. 
+ * Returns the baseline at the given offset, in pixels.
  *
  * @param offset the offset
- * 
+ *
  * @return baseline the baseline
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
- * </ul>
- *  
+ *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
+ * </ul>
+ *
  * @since 3.2
  */
 public int getBaseline(int offset) {
@@ -3103,7 +3219,7 @@
     int lineIndex = content.getLineAtOffset(offset);
     int lineOffset = content.getOffsetAtLine(lineIndex);
     TextLayout layout = renderer.getTextLayout(lineIndex);
-    int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
+    int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length));
     FontMetrics metrics = layout.getLineMetrics(lineInParagraph);
     renderer.disposeTextLayout(layout);
     return metrics.getAscent() + metrics.getLeading();
@@ -3118,14 +3234,14 @@
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * 
+ *
  * @deprecated use BidiSegmentListener instead.
  */
 public bool getBidiColoring() {
     checkWidget();
     return bidiColoring;
 }
-/** 
+/**
  * Returns the index of the last fully visible line.
  *
  * @return index of the last fully visible line.
@@ -3161,7 +3277,7 @@
     int lineOffset = content.getOffsetAtLine(lineIndex);
     String line = content.getLine(lineIndex);
     Rectangle bounds;
-    if (line.length() !is 0) {
+    if (line.length !is 0) {
         int offsetInLine = offset - lineOffset;
         TextLayout layout = renderer.getTextLayout(lineIndex);
         bounds = layout.getBounds(offsetInLine, offsetInLine);
@@ -3170,7 +3286,7 @@
         bounds = new Rectangle (0, 0, 0, renderer.getLineHeight());
     }
     if (offset is caretOffset) {
-        int lineEnd = lineOffset + line.length();
+        int lineEnd = lineOffset + line.length;
         if (offset is lineEnd && caretAlignment is PREVIOUS_OFFSET_TRAILING) {
             bounds.width += getCaretWidth();
         }
@@ -3207,7 +3323,7 @@
     return clipboard.getContents(plainTextTransfer, clipboardType);
 }
 int getClusterNext(int offset, int lineIndex) {
-    int lineOffset = content.getOffsetAtLine(lineIndex);    
+    int lineOffset = content.getOffsetAtLine(lineIndex);
     TextLayout layout = renderer.getTextLayout(lineIndex);
     offset -= lineOffset;
     offset = layout.getNextOffset(offset, DWT.MOVEMENT_CLUSTER);
@@ -3216,7 +3332,7 @@
     return offset;
 }
 int getClusterPrevious(int offset, int lineIndex) {
-    int lineOffset = content.getOffsetAtLine(lineIndex);    
+    int lineOffset = content.getOffsetAtLine(lineIndex);
     TextLayout layout = renderer.getTextLayout(lineIndex);
     offset -= lineOffset;
     offset = layout.getPreviousOffset(offset, DWT.MOVEMENT_CLUSTER);
@@ -3226,9 +3342,9 @@
 }
 /**
  * Returns the content implementation that is used for text storage.
- * 
+ *
  * @return content the user defined content implementation that is used for
- * text storage or the default content implementation if no user defined 
+ * text storage or the default content implementation if no user defined
  * content implementation has been set.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -3239,12 +3355,12 @@
     checkWidget();
     return content;
 }
-public bool getDragDetect () {
+public override bool getDragDetect () {
     checkWidget ();
-    return dragDetect;
-}
-/** 
- * Returns whether the widget : double click mouse behavior.
+    return dragDetect_;
+}
+/**
+ * Returns whether the widget implements double click mouse behavior.
  *
  * @return true if double clicking a word selects the word, false if double clicks
  * have the same effect as regular mouse clicks
@@ -3270,22 +3386,22 @@
     checkWidget();
     return editable;
 }
-public Color getForeground() {
+public override Color getForeground() {
     checkWidget();
     if (foreground is null) {
         return getDisplay().getSystemColor(DWT.COLOR_LIST_FOREGROUND);
     }
     return foreground;
 }
-/** 
+/**
  * Returns the horizontal scroll increment.
  *
  * @return horizontal scroll increment.
  */
-int getHorizontalIncrement() {  
+int getHorizontalIncrement() {
     return renderer.averageCharWidth;
 }
-/** 
+/**
  * Returns the horizontal scroll offset relative to the start of the line.
  *
  * @return horizontal scroll offset relative to the start of the line,
@@ -3299,7 +3415,7 @@
     checkWidget();
     return horizontalScrollOffset / getHorizontalIncrement();
 }
-/** 
+/**
  * Returns the horizontal scroll offset relative to the start of the line.
  *
  * @return the horizontal scroll offset relative to the start of the line,
@@ -3315,16 +3431,16 @@
 }
 /**
  * Returns the line indentation of the widget.
- * 
+ *
  * @return the line indentation
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *  
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @see #getLineIndent(int)
- * 
+ *
  * @since 3.2
  */
 public int getIndent() {
@@ -3333,32 +3449,32 @@
 }
 /**
  * Returns whether the widget justifies lines.
- * 
+ *
  * @return whether lines are justified
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *  
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @see #getLineJustify(int)
- * 
+ *
  * @since 3.2
  */
 public bool getJustify() {
     checkWidget();
     return justify;
 }
-/** 
+/**
  * Returns the action assigned to the key.
  * Returns DWT.NULL if there is no action associated with the key.
  *
- * @param key a key code defined in DWT.java or a character. 
+ * @param key a key code defined in DWT.java or a character.
  *  Optionally ORd with a state mask.  Preferred state masks are one or more of
- *  DWT.MOD1, DWT.MOD2, DWT.MOD3, since these masks account for modifier platform 
+ *  DWT.MOD1, DWT.MOD2, DWT.MOD3, since these masks account for modifier platform
  *  differences.  However, there may be cases where using the specific state masks
  *  (i.e., DWT.CTRL, DWT.SHIFT, DWT.ALT, DWT.COMMAND) makes sense.
- * @return one of the predefined actions defined in ST.java or DWT.NULL 
+ * @return one of the predefined actions defined in ST.java or DWT.NULL
  *  if there is no action associated with the key.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -3367,8 +3483,10 @@
  */
 public int getKeyBinding(int key) {
     checkWidget();
-    Integer action = cast(Integer) keyActionMap.get(new Integer(key));  
-    return action is null ? DWT.NULL : action.intValue();
+    if( auto p = key in keyActionMap ){
+        return *p;
+    }
+    return DWT.NULL;
 }
 /**
  * Gets the number of characters.
@@ -3386,36 +3504,36 @@
 /**
  * Returns the line at the given line index without delimiters.
  * Index 0 is the first line of the content. When there are not
- * any lines, getLine(0) is a valid call that answers an empty String.
+ * any lines, getLine(0) is a valid call that answers an empty string.
  * <p>
  *
  * @param lineIndex index of the line to return.
  * @return the line text without delimiters
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li> 
+ *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
  * </ul>
  * @since 3.4
  */
 public String getLine(int lineIndex) {
     checkWidget();
-    if (lineIndex < 0 || 
+    if (lineIndex < 0 ||
         (lineIndex > 0 && lineIndex >= content.getLineCount())) {
-        DWT.error(DWT.ERROR_INVALID_RANGE);     
+        DWT.error(DWT.ERROR_INVALID_RANGE);
     }
     return content.getLine(lineIndex);
 }
 /**
  * Returns the alignment of the line at the given index.
- * 
+ *
  * @param index the index of the line
- * 
+ *
  * @return the line alignment
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -3423,9 +3541,9 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
  * </ul>
- * 
+ *
  * @see #getAlignment()
- * 
+ *
  * @since 3.2
  */
 public int getLineAlignment(int index) {
@@ -3440,7 +3558,7 @@
  * where 0 &lt; offset &lt; getCharCount() so that getLineAtOffset(getCharCount())
  * returns the line of the insert location.
  *
- * @param offset offset relative to the start of the content. 
+ * @param offset offset relative to the start of the content.
  *  0 <= offset <= getCharCount()
  * @return line at the specified offset in the text
  * @exception DWTException <ul>
@@ -3448,26 +3566,26 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
+ *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
  * </ul>
  */
 public int getLineAtOffset(int offset) {
-    checkWidget();  
+    checkWidget();
     if (offset < 0 || offset > getCharCount()) {
-        DWT.error(DWT.ERROR_INVALID_RANGE);     
+        DWT.error(DWT.ERROR_INVALID_RANGE);
     }
     return content.getLineAtOffset(offset);
 }
 /**
  * Returns the background color of the line at the given index.
- * Returns null if a LineBackgroundListener has been set or if no background 
+ * Returns null if a LineBackgroundListener has been set or if no background
  * color has been specified for the line. Should not be called if a
  * LineBackgroundListener has been set since the listener maintains the
  * line background colors.
- * 
+ *
  * @param index the index of the line
  * @return the background color of the line at the given index.
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -3485,11 +3603,11 @@
 }
 /**
  * Returns the bullet of the line at the given index.
- * 
+ *
  * @param index the index of the line
- * 
+ *
  * @return the line bullet
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -3497,7 +3615,7 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
  * </ul>
- * 
+ *
  * @since 3.2
  */
 public Bullet getLineBullet(int index) {
@@ -3508,9 +3626,9 @@
     return isListening(LineGetStyle) ? null : renderer.getLineBullet(index, null);
 }
 /**
- * Returns the line background data for the given line or null if 
+ * Returns the line background data for the given line or null if
  * there is none.
- * 
+ *
  * @param lineOffset offset of the line start relative to the start
  *  of the content.
  * @param line line to get line background data for
@@ -3519,7 +3637,7 @@
 StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
     return sendLineEvent(LineGetBackground, lineOffset, line);
 }
-/** 
+/**
  * Gets the number of text lines.
  *
  * @return the number of lines in the widget
@@ -3533,10 +3651,10 @@
     return content.getLineCount();
 }
 /**
- * Returns the number of lines that can be completely displayed in the 
+ * Returns the number of lines that can be completely displayed in the
  * widget client area.
  *
- * @return number of lines that can be completely displayed in the widget 
+ * @return number of lines that can be completely displayed in the widget
  *  client area.
  */
 int getLineCountWhole() {
@@ -3564,7 +3682,7 @@
 /**
  * Returns the line height.
  * <p>
- * Note: this API should not be used if a StyleRange attribute causes lines to 
+ * Note: this API should not be used if a StyleRange attribute causes lines to
  * have different heights (i.e. different fonts, rise, etc).
  * </p>
  *
@@ -3583,17 +3701,17 @@
  * Returns the line height at the given offset.
  *
  * @param offset the offset
- *  
+ *
  * @return line height in pixels
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
- * </ul> 
- * 
+ *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
+ * </ul>
+ *
  * @since 3.2
  */
 public int getLineHeight(int offset) {
@@ -3607,18 +3725,18 @@
     int lineIndex = content.getLineAtOffset(offset);
     int lineOffset = content.getOffsetAtLine(lineIndex);
     TextLayout layout = renderer.getTextLayout(lineIndex);
-    int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length()));
+    int lineInParagraph = layout.getLineIndex(Math.min(offset - lineOffset, layout.getText().length));
     int height = layout.getLineBounds(lineInParagraph).height;
     renderer.disposeTextLayout(layout);
     return height;
 }
 /**
  * Returns the indentation of the line at the given index.
- * 
+ *
  * @param index the index of the line
- * 
+ *
  * @return the line indentation
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -3626,9 +3744,9 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
  * </ul>
- * 
+ *
  * @see #getIndent()
- * 
+ *
  * @since 3.2
  */
 public int getLineIndent(int index) {
@@ -3640,11 +3758,11 @@
 }
 /**
  * Returns whether the line at the given index is justified.
- * 
+ *
  * @param index the index of the line
- * 
- * @return whether the line is justified 
- * 
+ *
+ * @return whether the line is justified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -3652,9 +3770,9 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
  * </ul>
- * 
+ *
  * @see #getJustify()
- * 
+ *
  * @since 3.2
  */
 public bool getLineJustify(int index) {
@@ -3662,18 +3780,18 @@
     if (index < 0 || index > content.getLineCount()) {
         DWT.error(DWT.ERROR_INVALID_ARGUMENT);
     }
-    return isListening(LineGetStyle) ? false : renderer.getLineJustify(index, justify); 
+    return isListening(LineGetStyle) ? false : renderer.getLineJustify(index, justify);
 }
 /**
  * Returns the line spacing of the widget.
- * 
+ *
  * @return the line spacing
- *  
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @since 3.2
  */
 public int getLineSpacing() {
@@ -3681,18 +3799,18 @@
     return lineSpacing;
 }
 /**
- * Returns the line style data for the given line or null if there is 
+ * Returns the line style data for the given line or null if there is
  * none.
  * <p>
- * If there is a LineStyleListener but it does not set any styles, 
- * the StyledTextEvent.styles field will be initialized to an empty 
+ * If there is a LineStyleListener but it does not set any styles,
+ * the StyledTextEvent.styles field will be initialized to an empty
  * array.
  * </p>
- * 
- * @param lineOffset offset of the line start relative to the start of 
+ *
+ * @param lineOffset offset of the line start relative to the start of
  *  the content.
  * @param line line to get line styles for
- * @return line style data for the given line. Styles may start before 
+ * @return line style data for the given line. Styles may start before
  *  line start and end after line end
  */
 StyledTextEvent getLineStyleData(int lineOffset, String line) {
@@ -3701,11 +3819,13 @@
 /**
  * Returns the top pixel, relative to the client area, of a given line.
  * Clamps out of ranges index.
- *  
+ *
  * @param lineIndex the line index, the max value is lineCount. If
  * lineIndex is lineCount it returns the bottom pixel of the last line.
- * It means this function can be used to retrieve the bottom pixel of any line. 
- * 
+ * It means this function can be used to retrieve the bottom pixel of any line.
+ *
+ * @return the top pixel of a given line index
+ *
  * @since 3.2
  */
 public int getLinePixel(int lineIndex) {
@@ -3733,6 +3853,10 @@
  * Returns the line index for a y, relative to the client area.
  * The line index returned is always in the range 0..lineCount - 1.
  *
+ * @param y the y-coordinate pixel
+ *
+ * @return the line index for a given y-coordinate pixel
+ *
  * @since 3.2
  */
 public int getLineIndex(int y) {
@@ -3762,59 +3886,59 @@
     return line;
 }
 /**
- * Returns the x, y location of the upper left corner of the character 
- * bounding box at the specified offset in the text. The point is 
+ * Returns the x, y location of the upper left corner of the character
+ * bounding box at the specified offset in the text. The point is
  * relative to the upper left corner of the widget client area.
  *
- * @param offset offset relative to the start of the content. 
+ * @param offset offset relative to the start of the content.
  *  0 <= offset <= getCharCount()
- * @return x, y location of the upper left corner of the character 
+ * @return x, y location of the upper left corner of the character
  *  bounding box at the specified offset in the text.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li> 
+ *   <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
  * </ul>
  */
 public Point getLocationAtOffset(int offset) {
     checkWidget();
     if (offset < 0 || offset > getCharCount()) {
-        DWT.error(DWT.ERROR_INVALID_RANGE);     
+        DWT.error(DWT.ERROR_INVALID_RANGE);
     }
     return getPointAtOffset(offset);
 }
 /**
  * Returns the character offset of the first character of the given line.
  *
- * @param lineIndex index of the line, 0 based relative to the first 
+ * @param lineIndex index of the line, 0 based relative to the first
  *  line in the content. 0 <= lineIndex < getLineCount(), except
  *  lineIndex may always be 0
  * @return offset offset of the first character of the line, relative to
  *  the beginning of the document. The first character of the document is
- *  at offset 0.  
- *  When there are not any lines, getOffsetAtLine(0) is a valid call that 
+ *  at offset 0.
+ *  When there are not any lines, getOffsetAtLine(0) is a valid call that
  *  answers 0.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li> 
+ *   <li>ERROR_INVALID_RANGE when the line index is outside the valid range (< 0 or >= getLineCount())</li>
  * </ul>
  * @since 2.0
  */
 public int getOffsetAtLine(int lineIndex) {
     checkWidget();
-    if (lineIndex < 0 || 
+    if (lineIndex < 0 ||
         (lineIndex > 0 && lineIndex >= content.getLineCount())) {
-        DWT.error(DWT.ERROR_INVALID_RANGE);     
+        DWT.error(DWT.ERROR_INVALID_RANGE);
     }
     return content.getOffsetAtLine(lineIndex);
 }
 /**
- * Returns the offset of the character at the given location relative 
+ * Returns the offset of the character at the given location relative
  * to the first character in the document.
  * <p>
  * The return value reflects the character offset that the caret will
@@ -3823,9 +3947,9 @@
  * the returned offset will be behind the character.
  * </p>
  *
- * @param point the origin of character bounding box relative to 
+ * @param point the origin of character bounding box relative to
  *  the origin of the widget client area.
- * @return offset of the character at the given location relative 
+ * @return offset of the character at the given location relative
  *  to the first character in the document.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -3864,7 +3988,7 @@
 int getOffsetAtPoint(int x, int y, int lineIndex) {
     TextLayout layout = renderer.getTextLayout(lineIndex);
     x += horizontalScrollOffset - leftMargin;
-    int[] trailing = new int[1];    
+    int[1] trailing;
     int offsetInLine = layout.getOffset(x, y, trailing);
     caretAlignment = OFFSET_LEADING;
     if (trailing[0] !is 0) {
@@ -3874,11 +3998,11 @@
             offsetInLine += trailing[0];
             caretAlignment = PREVIOUS_OFFSET_TRAILING;
         } else {
-            String line = content.getLine(lineIndex);           
+            String line = content.getLine(lineIndex);
             int level;
             int offset = offsetInLine;
-            while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
-            if (offset is 0 && Character.isDigit(line.charAt(offset))) {
+            while (offset > 0 && tango.text.Unicode.isDigit(line[offset])) offset--;
+            if (offset is 0 && tango.text.Unicode.isDigit(line[offset])) {
                 level = isMirrored() ? 1 : 0;
             } else {
                 level = layout.getLevel(offset) & 0x1;
@@ -3906,7 +4030,7 @@
     }
     int lineIndex = getLineIndex(y);
     int lineOffset = content.getOffsetAtLine(lineIndex);
-    TextLayout layout = renderer.getTextLayout(lineIndex);  
+    TextLayout layout = renderer.getTextLayout(lineIndex);
     x += horizontalScrollOffset - leftMargin ;
     y -= getLinePixel(lineIndex);
     int offset = layout.getOffset(x, y, trailing);
@@ -3921,19 +4045,19 @@
  * Returns the orientation of the receiver.
  *
  * @return the orientation style
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @since 2.1.2
  */
 public int getOrientation () {
     checkWidget();
     return isMirrored() ? DWT.RIGHT_TO_LEFT : DWT.LEFT_TO_RIGHT;
 }
-/** 
+/**
  * Returns the index of the last partially visible line.
  *
  * @return index of the last partially visible line.
@@ -3946,7 +4070,7 @@
     }
     return getLineIndex(clientAreaHeight - bottomMargin);
 }
-/** 
+/**
  * Returns the index of the first partially visible line.
  *
  * @return index of the first partially visible line.
@@ -3959,11 +4083,11 @@
     return topIndexY <= 0 ? topIndex : topIndex - 1;
 }
 /**
- * Returns the content in the specified range using the platform line 
+ * Returns the content in the specified range using the platform line
  * delimiter to separate lines.
  *
  * @param writer the TextWriter to write line text into
- * @return the content in the specified range using the platform line 
+ * @return the content in the specified range using the platform line
  *  delimiter to separate lines as written by the specified TextWriter.
  */
 String getPlatformDelimitedText(TextWriter writer) {
@@ -3972,14 +4096,14 @@
     int endLine = content.getLineAtOffset(end);
     String endLineText = content.getLine(endLine);
     int endLineOffset = content.getOffsetAtLine(endLine);
-    
+
     for (int i = startLine; i <= endLine; i++) {
         writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
         if (i < endLine) {
             writer.writeLineDelimiter(PlatformLineDelimiter);
         }
     }
-    if (end > endLineOffset + endLineText.length()) {
+    if (end > endLineOffset + endLineText.length) {
         writer.writeLineDelimiter(PlatformLineDelimiter);
     }
     writer.close();
@@ -3987,8 +4111,8 @@
 }
 /**
  * Returns all the ranges of text that have an associated StyleRange.
- * Returns an empty array if a LineStyleListener has been set. 
- * Should not be called if a LineStyleListener has been set since the 
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * <p>
  * The ranges array contains start and length pairs.  Each pair refers to
@@ -3996,16 +4120,16 @@
  * that starts at ranges[n] with length ranges[n+1] uses the style
  * at styles[n/2] returned by <code>getStyleRanges(int, int, bool)</code>.
  * </p>
- * 
+ *
  * @return the ranges or an empty array if a LineStyleListener has been set.
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @since 3.2
- * 
+ *
  * @see #getStyleRanges(bool)
  */
 public int[] getRanges() {
@@ -4018,8 +4142,8 @@
 }
 /**
  * Returns the ranges of text that have an associated StyleRange.
- * Returns an empty array if a LineStyleListener has been set. 
- * Should not be called if a LineStyleListener has been set since the 
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * <p>
  * The ranges array contains start and length pairs.  Each pair refers to
@@ -4030,19 +4154,19 @@
  *
  * @param start the start offset of the style ranges to return
  * @param length the number of style ranges to return
- * 
+ *
  * @return the ranges or an empty array if a LineStyleListener has been set.
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li> 
- * </ul> 
- * 
+ *   <li>ERROR_INVALID_RANGE if start or length are outside the widget content</li>
+ * </ul>
+ *
  * @since 3.2
- * 
+ *
  * @see #getStyleRanges(int, int, bool)
  */
 public int[] getRanges(int start, int length) {
@@ -4062,15 +4186,15 @@
  * Returns the selection.
  * <p>
  * Text selections are specified in terms of caret positions.  In a text
- * widget that contains N characters, there are N+1 caret positions, 
+ * widget that contains N characters, there are N+1 caret positions,
  * ranging from 0..N
  * </p>
  *
- * @return start and end of the selection, x is the offset of the first 
+ * @return start and end of the selection, x is the offset of the first
  *  selected character, y is the offset after the last selected character.
- *  The selection values returned are visual (i.e., x will always always be   
- *  <= y).  To determine if a selection is right-to-left cast(RtoL) vs. left-to-right 
- *  cast(LtoR), compare the caretOffset to the start and end of the selection 
+ *  The selection values returned are visual (i.e., x will always always be
+ *  <= y).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
+ *  (LtoR), compare the caretOffset to the start and end of the selection
  *  (e.g., caretOffset is start of selection implies that the selection is RtoL).
  * @see #getSelectionRange
  * @exception DWTException <ul>
@@ -4085,12 +4209,12 @@
 /**
  * Returns the selection.
  *
- * @return start and length of the selection, x is the offset of the 
- *  first selected character, relative to the first character of the 
- *  widget content. y is the length of the selection. 
- *  The selection values returned are visual (i.e., length will always always be   
- *  positive).  To determine if a selection is right-to-left cast(RtoL) vs. left-to-right 
- *  cast(LtoR), compare the caretOffset to the start and end of the selection 
+ * @return start and length of the selection, x is the offset of the
+ *  first selected character, relative to the first character of the
+ *  widget content. y is the length of the selection.
+ *  The selection values returned are visual (i.e., length will always always be
+ *  positive).  To determine if a selection is right-to-left (RtoL) vs. left-to-right
+ *  (LtoR), compare the caretOffset to the start and end of the selection
  *  (e.g., caretOffset is start of selection implies that the selection is RtoL).
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -4163,7 +4287,7 @@
     checkWidget();
     return content.getTextRange(selection.x, selection.y - selection.x);
 }
-public int getStyle() {
+public override int getStyle() {
     int style = super.getStyle();
     style &= ~(DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT | DWT.MIRRORED);
     if (isMirrored()) {
@@ -4175,18 +4299,18 @@
 }
 
 /**
- * Returns the text segments that should be treated as if they 
+ * Returns the text segments that should be treated as if they
  * had a different direction than the surrounding text.
  *
- * @param lineOffset offset of the first character in the line. 
+ * @param lineOffset offset of the first character in the line.
  *  0 based from the beginning of the document.
  * @param line text of the line to specify bidi segments for
  * @return text segments that should be treated as if they had a
- *  different direction than the surrounding text. Only the start 
- *  index of a segment is specified, relative to the start of the 
- *  line. Always starts with 0 and ends with the line length. 
+ *  different direction than the surrounding text. Only the start
+ *  index of a segment is specified, relative to the start of the
+ *  line. Always starts with 0 and ends with the line length.
  * @exception IllegalArgumentException <ul>
- *    <li>ERROR_INVALID_ARGUMENT - if the segment indices returned 
+ *    <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
  *      by the listener do not start with 0, are not in ascending order,
  *      exceed the line length or have duplicates</li>
  * </ul>
@@ -4197,21 +4321,21 @@
         return getBidiSegmentsCompatibility(line, lineOffset);
     }
     StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
-    int lineLength = line.length();
+    int lineLength = line.length;
     int[] segments;
     if (event is null || event.segments is null || event.segments.length is 0) {
-        segments = new int[] {0, lineLength};
+        segments = [0, lineLength];
     } else {
         int segmentCount = event.segments.length;
-        
+
         // test segment index consistency
         if (event.segments[0] !is 0) {
             DWT.error(DWT.ERROR_INVALID_ARGUMENT);
-        }   
+        }
         for (int i = 1; i < segmentCount; i++) {
             if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
                 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
-            }   
+            }
         }
         // ensure that last segment index is line end offset
         if (event.segments[segmentCount - 1] !is lineLength) {
@@ -4229,9 +4353,9 @@
  * Supports deprecated setBidiColoring API. Remove when API is removed.
  */
 int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
-    int lineLength = line.length();
+    int lineLength = line.length;
     if (!bidiColoring) {
-        return new int[] {0, lineLength};
+        return [0, lineLength];
     }
     StyleRange [] styles = null;
     StyledTextEvent event = getLineStyleData(lineOffset, line);
@@ -4241,7 +4365,7 @@
         styles = renderer.getStyleRanges(lineOffset, lineLength, true);
     }
     if (styles is null || styles.length is 0) {
-        return new int[] {0, lineLength};
+        return [0, lineLength];
     }
     int k=0, count = 1;
     while (k < styles.length && styles[k].start is 0 && styles[k].length is lineLength) {
@@ -4252,7 +4376,7 @@
         StyleRange style = styles[i];
         int styleLineStart = Math.max(style.start - lineOffset, 0);
         int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
-        styleLineEnd = Math.min (styleLineEnd, line.length ());
+        styleLineEnd = Math.min (styleLineEnd, line.length );
         if (i > 0 && count > 1 &&
             ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
              (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
@@ -4284,15 +4408,15 @@
  * Returns the style range at the given offset.
  * <p>
  * Returns null if a LineStyleListener has been set or if a style is not set
- * for the offset. 
- * Should not be called if a LineStyleListener has been set since the 
+ * for the offset.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p>
  *
- * @param offset the offset to return the style for. 
+ * @param offset the offset to return the style for.
  *  0 <= offset < getCharCount() must be true.
  * @return a StyleRange with start is offset and length is 1, indicating
- *  the style at the given offset. null if a LineStyleListener has been set 
+ *  the style at the given offset. null if a LineStyleListener has been set
  *  or if a style is not set for the given offset.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -4316,8 +4440,8 @@
 /**
  * Returns the styles.
  * <p>
- * Returns an empty array if a LineStyleListener has been set. 
- * Should not be called if a LineStyleListener has been set since the 
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * <p></p>
  * Note: Because a StyleRange includes the start and length, the
@@ -4333,7 +4457,7 @@
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * 
+ *
  * @see #getStyleRanges(bool)
  */
 public StyleRange[] getStyleRanges() {
@@ -4343,8 +4467,8 @@
 /**
  * Returns the styles.
  * <p>
- * Returns an empty array if a LineStyleListener has been set. 
- * Should not be called if a LineStyleListener has been set since the 
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p><p>
  * Note: When <code>includeRanges</code> is true, the start and length
@@ -4353,18 +4477,18 @@
  * false, <code>getRanges(int, int)</code> can be used to get the
  * associated ranges.
  * </p>
- * 
+ *
  * @param includeRanges whether the start and length field of the StyleRanges should be set.
- * 
+ *
  * @return the styles or an empty array if a LineStyleListener has been set.
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @since 3.2
- * 
+ *
  * @see #getRanges(int, int)
  * @see #setStyleRanges(int[], StyleRange[])
  */
@@ -4375,8 +4499,8 @@
 /**
  * Returns the styles for the given text range.
  * <p>
- * Returns an empty array if a LineStyleListener has been set. 
- * Should not be called if a LineStyleListener has been set since the 
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p><p>
  * Note: Because the StyleRange includes the start and length, the
@@ -4388,10 +4512,10 @@
  * @param start the start offset of the style ranges to return
  * @param length the number of style ranges to return
  *
- * @return the styles or an empty array if a LineStyleListener has 
- *  been set.  The returned styles will reflect the given range.  The first 
- *  returned <code>StyleRange</code> will have a starting offset >= start 
- *  and the last returned <code>StyleRange</code> will have an ending 
+ * @return the styles or an empty array if a LineStyleListener has
+ *  been set.  The returned styles will reflect the given range.  The first
+ *  returned <code>StyleRange</code> will have a starting offset >= start
+ *  and the last returned <code>StyleRange</code> will have an ending
  *  offset <= start + length - 1
  *
  * @exception DWTException <ul>
@@ -4399,11 +4523,11 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
- * </ul>
- * 
+ *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
+ * </ul>
+ *
  * @see #getStyleRanges(int, int, bool)
- * 
+ *
  * @since 3.0
  */
 public StyleRange[] getStyleRanges(int start, int length) {
@@ -4413,8 +4537,8 @@
 /**
  * Returns the styles for the given text range.
  * <p>
- * Returns an empty array if a LineStyleListener has been set. 
- * Should not be called if a LineStyleListener has been set since the 
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p><p>
  * Note: When <code>includeRanges</code> is true, the start and length
@@ -4423,15 +4547,15 @@
  * false, <code>getRanges(int, int)</code> can be used to get the
  * associated ranges.
  * </p>
- * 
+ *
  * @param start the start offset of the style ranges to return
  * @param length the number of style ranges to return
  * @param includeRanges whether the start and length field of the StyleRanges should be set.
  *
- * @return the styles or an empty array if a LineStyleListener has 
- *  been set.  The returned styles will reflect the given range.  The first 
- *  returned <code>StyleRange</code> will have a starting offset >= start 
- *  and the last returned <code>StyleRange</code> will have an ending 
+ * @return the styles or an empty array if a LineStyleListener has
+ *  been set.  The returned styles will reflect the given range.  The first
+ *  returned <code>StyleRange</code> will have a starting offset >= start
+ *  and the last returned <code>StyleRange</code> will have an ending
  *  offset <= start + length - 1
  *
  * @exception DWTException <ul>
@@ -4439,11 +4563,11 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
- * </ul>
- * 
+ *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
+ * </ul>
+ *
  * @since 3.2
- * 
+ *
  * @see #getRanges(int, int)
  * @see #setStyleRanges(int[], StyleRange[])
  */
@@ -4490,7 +4614,7 @@
  * Returns the widget content between the two offsets.
  *
  * @param start offset of the first character in the returned String
- * @param end offset of the last character in the returned String 
+ * @param end offset of the last character in the returned String
  * @return widget content starting at start and ending at end
  * @see #getTextRange(int,int)
  * @exception DWTException <ul>
@@ -4498,7 +4622,7 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
+ *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
  * </ul>
  */
 public String getText(int start, int end) {
@@ -4513,20 +4637,20 @@
  * Returns the smallest bounding rectangle that includes the characters between two offsets.
  *
  * @param start offset of the first character included in the bounding box
- * @param end offset of the last character included in the bounding box 
+ * @param end offset of the last character included in the bounding box
  * @return bounding box of the text between start and end
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
+ *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
  * </ul>
  * @since 3.1
  */
 public Rectangle getTextBounds(int start, int end) {
-    checkWidget();  
-    int contentLength = getCharCount(); 
+    checkWidget();
+    int contentLength = getCharCount();
     if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
         DWT.error(DWT.ERROR_INVALID_RANGE);
     }
@@ -4537,9 +4661,9 @@
     int height = 0;
     int left = 0x7fffffff, right = 0;
     for (int i = lineStart; i <= lineEnd; i++) {
-        int lineOffset = content.getOffsetAtLine(i);        
+        int lineOffset = content.getOffsetAtLine(i);
         TextLayout layout = renderer.getTextLayout(i);
-        int length = layout.getText().length();
+        int length = layout.getText().length;
         if (length > 0) {
             if (i is lineStart) {
                 if (i is lineEnd) {
@@ -4569,14 +4693,14 @@
  * Returns the widget content starting at start for length characters.
  *
  * @param start offset of the first character in the returned String
- * @param length number of characters to return 
+ * @param length number of characters to return
  * @return widget content starting at start and extending length characters.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li> 
+ *   <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
  * </ul>
  */
 public String getTextRange(int start, int length) {
@@ -4585,12 +4709,12 @@
     int end = start + length;
     if (start > end || start < 0 || end > contentLength) {
         DWT.error(DWT.ERROR_INVALID_RANGE);
-    }   
+    }
     return content.getTextRange(start, length);
 }
 /**
  * Returns the maximum number of characters that the receiver is capable of holding.
- * 
+ *
  * @return the text limit
  *
  * @exception DWTException <ul>
@@ -4605,8 +4729,8 @@
 /**
  * Gets the top index.
  * <p>
- * The top index is the index of the fully visible line that is currently 
- * at the top of the widget or the topmost partially visible line if no line is fully visible. 
+ * The top index is the index of the fully visible line that is currently
+ * at the top of the widget or the topmost partially visible line if no line is fully visible.
  * The top index changes when the widget is scrolled. Indexing is zero based.
  * </p>
  *
@@ -4623,10 +4747,10 @@
 /**
  * Gets the top pixel.
  * <p>
- * The top pixel is the pixel position of the line that is 
- * currently at the top of the widget. The text widget can be scrolled by pixels 
- * by dragging the scroll thumb so that a partial line may be displayed at the top 
- * the widget.  The top pixel changes when the widget is scrolled.  The top pixel 
+ * The top pixel is the pixel position of the line that is
+ * currently at the top of the widget. The text widget can be scrolled by pixels
+ * by dragging the scroll thumb so that a partial line may be displayed at the top
+ * the widget.  The top pixel changes when the widget is scrolled.  The top pixel
  * does not include the widget trimming.
  * </p>
  *
@@ -4640,7 +4764,7 @@
     checkWidget();
     return getVerticalScrollOffset();
 }
-/** 
+/**
  * Returns the vertical scroll increment.
  *
  * @return vertical scroll increment.
@@ -4679,12 +4803,12 @@
     int lineOffset = content.getOffsetAtLine(caretLine);
     String line = content.getLine(caretLine);
     int offset = caretOffset - lineOffset;
-    int lineLength = line.length();
+    int lineLength = line.length;
     if (lineLength is 0) return isMirrored() ? DWT.RIGHT : DWT.LEFT;
     if (caretAlignment is PREVIOUS_OFFSET_TRAILING && offset > 0) offset--;
     if (offset is lineLength && offset > 0) offset--;
-    while (offset > 0 && Character.isDigit(line.charAt(offset))) offset--;
-    if (offset is 0 && Character.isDigit(line.charAt(offset))) {
+    while (offset > 0 && tango.text.Unicode.isDigit(line[offset])) offset--;
+    if (offset is 0 && tango.text.Unicode.isDigit(line[offset])) {
         return isMirrored() ? DWT.RIGHT : DWT.LEFT;
     }
     TextLayout layout = renderer.getTextLayout(caretLine);
@@ -4717,7 +4841,7 @@
         int lineIndex = content.getLineAtOffset(offset);
         lineOffset = content.getOffsetAtLine(lineIndex);
         lineText = content.getLine(lineIndex);
-        int lineLength = lineText.length();
+        int lineLength = lineText.length;
         if (offset is lineOffset + lineLength) {
             newOffset = content.getOffsetAtLine(lineIndex + 1);
         } else {
@@ -4742,12 +4866,12 @@
         lineText = content.getLine(lineIndex);
         if (offset is lineOffset) {
             String nextLineText = content.getLine(lineIndex - 1);
-            int nextLineOffset = content.getOffsetAtLine(lineIndex - 1); 
-            newOffset = nextLineOffset + nextLineText.length();
+            int nextLineOffset = content.getOffsetAtLine(lineIndex - 1);
+            newOffset = nextLineOffset + nextLineText.length;
         } else {
             TextLayout layout = renderer.getTextLayout(lineIndex);
             newOffset = lineOffset + layout.getPreviousOffset(offset - lineOffset, movement);
-            renderer.disposeTextLayout(layout); 
+            renderer.disposeTextLayout(layout);
         }
     }
     return sendWordBoundaryEvent(WordPrevious, movement, offset, newOffset, lineText, lineOffset);
@@ -4762,7 +4886,7 @@
     checkWidget();
     return wordWrap;
 }
-/** 
+/**
  * Returns the location of the given offset.
  * <p>
  * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
@@ -4775,7 +4899,7 @@
     String line = content.getLine(lineIndex);
     int lineOffset = content.getOffsetAtLine(lineIndex);
     int offsetInLine = offset - lineOffset;
-    int lineLength = line.length();
+    int lineLength = line.length;
     if (lineIndex < content.getLineCount() - 1) {
         int endLineOffset = content.getOffsetAtLine(lineIndex + 1) - 1;
         if (lineLength < offsetInLine && offsetInLine <= endLineOffset) {
@@ -4786,7 +4910,9 @@
     TextLayout layout = renderer.getTextLayout(lineIndex);
     if (lineLength !is 0  && offsetInLine <= lineLength) {
         if (offsetInLine is lineLength) {
-            point = layout.getLocation(offsetInLine - 1, true);
+            // DWT: Instead of go back one byte, go back one codepoint
+            int offsetInLine_m1 = layout.getPreviousOffset(offsetInLine, DWT.MOVEMENT_CLUSTER);
+            point = layout.getLocation(offsetInLine_m1, true);
         } else {
             switch (caretAlignment) {
                 case OFFSET_LEADING:
@@ -4797,7 +4923,9 @@
                     if (offsetInLine is 0) {
                         point = layout.getLocation(offsetInLine, false);
                     } else {
-                        point = layout.getLocation(offsetInLine - 1, true);
+                        // DWT: Instead of go back one byte, go back one codepoint
+                        int offsetInLine_m1 = layout.getPreviousOffset(offsetInLine, DWT.MOVEMENT_CLUSTER);
+                        point = layout.getLocation(offsetInLine_m1, true);
                     }
                     break;
             }
@@ -4810,32 +4938,30 @@
     point.y += getLinePixel(lineIndex);
     return point;
 }
-/** 
- * Inserts a String.  The old selection is replaced with the new text.  
- *
- * @param String the String
+/**
+ * Inserts a string.  The old selection is replaced with the new text.
+ *
+ * @param string the string
  * @see #replaceTextRange(int,int,String)
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * @exception IllegalArgumentException <ul>
- *    <li>ERROR_NULL_ARGUMENT when String is null</li>
- * </ul>
- */
-public void insert(String String) {
-    checkWidget();
-    if (String is null) {
-        DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    }
+ */
+public void insert(String string) {
+    checkWidget();
+    // DWT extension: allow null for zero length string
+//     if (string is null) {
+//         DWT.error(DWT.ERROR_NULL_ARGUMENT);
+//     }
     Point sel = getSelectionRange();
-    replaceTextRange(sel.x, sel.y, String);
+    replaceTextRange(sel.x, sel.y, string);
 }
 /**
  * Creates content change listeners and set the default content model.
  */
 void installDefaultContent() {
-    textChangeListener = new TextChangeListener() {
+    textChangeListener = new class() TextChangeListener {
         public void textChanging(TextChangingEvent event) {
             handleTextChanging(event);
         }
@@ -4849,14 +4975,14 @@
     content = new DefaultContent();
     content.addTextChangeListener(textChangeListener);
 }
-/** 
+/**
  * Adds event listeners
  */
 void installListeners() {
     ScrollBar verticalBar = getVerticalBar();
     ScrollBar horizontalBar = getHorizontalBar();
-    
-    listener = new Listener() {
+
+    listener = new class() Listener {
         public void handleEvent(Event event) {
             switch (event.type) {
                 case DWT.Dispose: handleDispose(event); break;
@@ -4868,8 +4994,9 @@
                 case DWT.Paint: handlePaint(event); break;
                 case DWT.Resize: handleResize(event); break;
                 case DWT.Traverse: handleTraverse(event); break;
+                default:
             }
-        }       
+        }
     };
     addListener(DWT.Dispose, listener);
     addListener(DWT.KeyDown, listener);
@@ -4880,24 +5007,25 @@
     addListener(DWT.Paint, listener);
     addListener(DWT.Resize, listener);
     addListener(DWT.Traverse, listener);
-    ime.addListener(DWT.ImeComposition, new Listener() {
+    ime.addListener(DWT.ImeComposition, new class() Listener {
         public void handleEvent(Event event) {
             switch (event.detail) {
                 case DWT.COMPOSITION_SELECTION: handleCompositionSelection(event); break;
                 case DWT.COMPOSITION_CHANGED: handleCompositionChanged(event); break;
                 case DWT.COMPOSITION_OFFSET: handleCompositionOffset(event); break;
+                default:
             }
         }
     });
     if (verticalBar !is null) {
-        verticalBar.addListener(DWT.Selection, new Listener() {
+        verticalBar.addListener(DWT.Selection, new class() Listener {
             public void handleEvent(Event event) {
                 handleVerticalScroll(event);
             }
         });
     }
     if (horizontalBar !is null) {
-        horizontalBar.addListener(DWT.Selection, new Listener() {
+        horizontalBar.addListener(DWT.Selection, new class() Listener {
             public void handleEvent(Event event) {
                 handleHorizontalScroll(event);
             }
@@ -4930,8 +5058,8 @@
     TextLayout layout = renderer.getTextLayout(startLine);
     int lineX = leftMargin - horizontalScrollOffset, startLineY = getLinePixel(startLine);
     int[] offsets = layout.getLineOffsets();
-    int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length()));
-    
+    int startIndex = layout.getLineIndex(Math.min(start, layout.getText().length));
+
     /* Redraw end of line before start line if wrapped and start offset is first char */
     if (wordWrap && startIndex > 0 && offsets[startIndex] is start) {
         Rectangle rect = layout.getLineBounds(startIndex - 1);
@@ -4941,9 +5069,9 @@
         rect.y += startLineY;
         super.redraw(rect.x, rect.y, rect.width, rect.height, false);
     }
-    
+
     if (startLine is endLine) {
-        int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
+        int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length));
         if (startIndex is endIndex) {
             /* Redraw rect between start and end offset if start and end offsets are in same wrapped line */
             Rectangle rect = layout.getBounds(start, end - 1);
@@ -4974,7 +5102,7 @@
         layout = renderer.getTextLayout(endLine);
         offsets = layout.getLineOffsets();
     }
-    int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length()));
+    int endIndex = layout.getLineIndex(Math.min(end, layout.getText().length));
     Rectangle endRect = layout.getBounds(offsets[endIndex], end - 1);
     if (endRect.height is 0) {
         Rectangle bounds = layout.getLineBounds(endIndex);
@@ -5006,26 +5134,27 @@
     String text = event.text;
     int start = event.start;
     int end = event.end;
-    int length = text.length();
+    int length = text.length;
     if (length is ime.getCommitCount()) {
         content.replaceTextRange(start, end - start, "");
-        caretOffset = start;
+        caretOffset = ime.getCompositionOffset();
         caretWidth = 0;
         caretDirection = DWT.NULL;
     } else {
         content.replaceTextRange(start, end - start, text);
         caretOffset = ime.getCaretOffset();
         if (ime.getWideCaret()) {
+            start = ime.getCompositionOffset();
             int lineIndex = getCaretLine();
             int lineOffset = content.getOffsetAtLine(lineIndex);
-            TextLayout layout = renderer.getTextLayout(lineIndex);  
+            TextLayout layout = renderer.getTextLayout(lineIndex);
             caretWidth = layout.getBounds(start - lineOffset, start + length - 1 - lineOffset).width;
             renderer.disposeTextLayout(layout);
         }
     }
     showCaret();
 }
-/** 
+/**
  * Frees resources.
  */
 void handleDispose(Event event) {
@@ -5067,7 +5196,7 @@
     foreground = null;
     clipboard = null;
 }
-/** 
+/**
  * Scrolls the widget horizontally.
  */
 void handleHorizontalScroll(Event event) {
@@ -5101,35 +5230,35 @@
     }
     if (action is DWT.NULL) {
         bool ignore = false;
-        
+
         if (IS_CARBON) {
-            // Ignore accelerator key combinations (we do not want to 
-            // insert a character in the text in this instance). Do not  
+            // Ignore accelerator key combinations (we do not want to
+            // insert a character in the text in this instance). Do not
             // ignore COMMAND+ALT combinations since that key sequence
             // produces characters on the mac.
             ignore = (event.stateMask ^ DWT.COMMAND) is 0 ||
                     (event.stateMask ^ (DWT.COMMAND | DWT.SHIFT)) is 0;
         } else if (IS_MOTIF) {
-            // Ignore accelerator key combinations (we do not want to 
-            // insert a character in the text in this instance). Do not  
+            // Ignore accelerator key combinations (we do not want to
+            // insert a character in the text in this instance). Do not
             // ignore ALT combinations since this key sequence
             // produces characters on motif.
             ignore = (event.stateMask ^ DWT.CTRL) is 0 ||
                     (event.stateMask ^ (DWT.CTRL | DWT.SHIFT)) is 0;
         } else {
-            // Ignore accelerator key combinations (we do not want to 
-            // insert a character in the text in this instance). Don't  
-            // ignore CTRL+ALT combinations since that is the Alt Gr 
-            // key on some keyboards.  See bug 20953. 
-            ignore = (event.stateMask ^ DWT.ALT) is 0 || 
+            // Ignore accelerator key combinations (we do not want to
+            // insert a character in the text in this instance). Don't
+            // ignore CTRL+ALT combinations since that is the Alt Gr
+            // key on some keyboards.  See bug 20953.
+            ignore = (event.stateMask ^ DWT.ALT) is 0 ||
                     (event.stateMask ^ DWT.CTRL) is 0 ||
                     (event.stateMask ^ (DWT.ALT | DWT.SHIFT)) is 0 ||
                     (event.stateMask ^ (DWT.CTRL | DWT.SHIFT)) is 0;
         }
         // -ignore anything below SPACE except for line delimiter keys and tab.
-        // -ignore DEL 
-        if (!ignore && event.character > 31 && event.character !is DWT.DEL || 
-            event.character is DWT.CR || event.character is DWT.LF || 
+        // -ignore DEL
+        if (!ignore && event.character > 31 && event.character !is DWT.DEL ||
+            event.character is DWT.CR || event.character is DWT.LF ||
             event.character is TAB) {
             doContent(event.character);
             update();
@@ -5148,7 +5277,7 @@
     if (clipboardSelection is null) {
         clipboardSelection = new Point(selection.x, selection.y);
     }
-    
+
     Event verifyEvent = new Event();
     verifyEvent.character = event.character;
     verifyEvent.keyCode = event.keyCode;
@@ -5172,7 +5301,7 @@
                     setClipboardContent(selection.x, selection.y - selection.x, DND.SELECTION_CLIPBOARD);
                 }
             } catch (DWTError error) {
-                // Copy to clipboard failed. This happens when another application 
+                // Copy to clipboard failed. This happens when another application
                 // is accessing the clipboard while we copy. Ignore the error.
                 // Fixes 1GDQAVN
                 // Rethrow all other errors. Fixes bug 17578.
@@ -5184,21 +5313,22 @@
     }
     clipboardSelection = null;
 }
-/** 
- * Updates the caret location and selection if mouse button 1 has been 
+/**
+ * Updates the caret location and selection if mouse button 1 has been
  * pressed.
  */
 void handleMouseDown(Event event) {
     //force focus (object support)
     forceFocus();
-        
+
     //drag detect
-    if (dragDetect && checkDragDetect(event)) return;
-        
+    if (dragDetect_ && checkDragDetect(event)) return;
+
     //paste clipboard selection
     if (event.button is 2) {
-        String text = cast(String)getClipboardContent(DND.SELECTION_CLIPBOARD);
-        if (text !is null && text.length() > 0) {
+        auto o = cast(ArrayWrapperString)getClipboardContent(DND.SELECTION_CLIPBOARD);
+        String text = o.array;
+        if (text !is null && text.length > 0) {
             // position cursor
             doMouseLocationChange(event.x, event.y, false);
             // insert text
@@ -5209,10 +5339,10 @@
             sendKeyEvent(e);
         }
     }
-    
+
     //set selection
     if ((event.button !is 1) || (IS_CARBON && (event.stateMask & DWT.MOD4) !is 0)) {
-        return; 
+        return;
     }
     clickCount = event.count;
     if (clickCount is 1) {
@@ -5245,8 +5375,8 @@
         }
     }
 }
-/** 
- * Updates the caret location and selection if mouse button 1 is pressed 
+/**
+ * Updates the caret location and selection if mouse button 1 is pressed
  * during the mouse move.
  */
 void handleMouseMove(Event event) {
@@ -5255,7 +5385,7 @@
     update();
     doAutoScroll(event);
 }
-/** 
+/**
  * Autoscrolling ends when the mouse button is released.
  */
 void handleMouseUp(Event event) {
@@ -5267,7 +5397,7 @@
                 setClipboardContent(selection.x, selection.y - selection.x, DND.SELECTION_CLIPBOARD);
             }
         } catch (DWTError error) {
-            // Copy to clipboard failed. This happens when another application 
+            // Copy to clipboard failed. This happens when another application
             // is accessing the clipboard while we copy. Ignore the error.
             // Fixes 1GDQAVN
             // Rethrow all other errors. Fixes bug 17578.
@@ -5319,7 +5449,7 @@
     }
 }
 /**
- * Recalculates the scroll bars. Rewraps all lines when in word 
+ * Recalculates the scroll bars. Rewraps all lines when in word
  * wrap mode.
  *
  * @param event resize event
@@ -5333,13 +5463,13 @@
     /* Redraw the old or new right/bottom margin if needed */
     if (oldWidth !is clientAreaWidth) {
         if (rightMargin > 0) {
-            int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin; 
+            int x = (oldWidth < clientAreaWidth ? oldWidth : clientAreaWidth) - rightMargin;
             super.redraw(x, 0, rightMargin, oldHeight, false);
         }
     }
     if (oldHeight !is clientAreaHeight) {
         if (bottomMargin > 0) {
-            int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin; 
+            int y = (oldHeight < clientAreaHeight ? oldHeight : clientAreaHeight) - bottomMargin;
             super.redraw(0, y, oldWidth, bottomMargin, false);
         }
     }
@@ -5378,10 +5508,14 @@
 //  }
 }
 /**
- * Updates the caret position and selection and the scroll bars to reflect 
+ * Updates the caret position and selection and the scroll bars to reflect
  * the content change.
  */
 void handleTextChanged(TextChangedEvent event) {
+    int offset = ime.getCompositionOffset();
+    if (offset !is -1 && lastTextChangeStart < offset) {
+        ime.setCompositionOffset(offset + lastTextChangeNewCharCount - lastTextChangeReplaceCharCount);
+    }
     int firstLine = content.getLineAtOffset(lastTextChangeStart);
     resetCache(firstLine, 0);
     if (!isFixedLineHeight() && topIndex > firstLine) {
@@ -5394,7 +5528,6 @@
         int newLastLineBottom = getLinePixel(lastLine + 1);
         if (lastLineBottom !is newLastLineBottom) {
             super.redraw();
-            if (wordWrap) setCaretLocation();
         } else {
             super.redraw(0, firstLineTop, clientAreaWidth, newLastLineBottom - firstLineTop, false);
             redrawLinesBullet(renderer.redrawLines);
@@ -5403,10 +5536,10 @@
     renderer.redrawLines = null;
     // update selection/caret location after styles have been changed.
     // otherwise any text measuring could be incorrect
-    // 
-    // also, this needs to be done after all scrolling. Otherwise, 
+    //
+    // also, this needs to be done after all scrolling. Otherwise,
     // selection redraw would be flushed during scroll which is wrong.
-    // in some cases new text would be drawn in scroll source area even 
+    // in some cases new text would be drawn in scroll source area even
     // though the intent is to scroll it.
     updateSelection(lastTextChangeStart, lastTextChangeReplaceCharCount, lastTextChangeNewCharCount);
     if (lastTextChangeReplaceLineCount > 0 || wordWrap) {
@@ -5420,7 +5553,7 @@
  * Updates the screen to reflect a pending content change.
  *
  * @param event .start the start offset of the change
- * @param event .newText text that is going to be inserted or empty String 
+ * @param event .newText text that is going to be inserted or empty String
  *  if no text will be inserted
  * @param event .replaceCharCount length of text that is going to be replaced
  * @param event .newCharCount length of text that is going to be inserted
@@ -5436,7 +5569,7 @@
     lastTextChangeNewLineCount = event.newLineCount;
     lastTextChangeNewCharCount = event.newCharCount;
     lastTextChangeReplaceLineCount = event.replaceLineCount;
-    lastTextChangeReplaceCharCount = event.replaceCharCount;    
+    lastTextChangeReplaceCharCount = event.replaceCharCount;
     int lineIndex = content.getLineAtOffset(event.start);
     int srcY = getLinePixel(lineIndex + event.replaceLineCount + 1);
     int destY = getLinePixel(lineIndex + 1) + event.newLineCount * renderer.getLineHeight();
@@ -5451,7 +5584,7 @@
     }
 
     renderer.textChanging(event);
-    
+
     // Update the caret offset if it is greater than the length of the content.
     // This is necessary since style range API may be called between the
     // handleTextChanging and handleTextChanged events and this API sets the
@@ -5460,20 +5593,20 @@
     if (caretOffset > newEndOfText) caretOffset = newEndOfText;
 }
 /**
- * Called when the widget content is set programmatically, overwriting 
- * the old content. Resets the caret position, selection and scroll offsets. 
+ * Called when the widget content is set programmatically, overwriting
+ * the old content. Resets the caret position, selection and scroll offsets.
  * Recalculates the content width and scroll bars. Redraws the widget.
  *
- * @param event text change event. 
+ * @param event text change event.
  */
 void handleTextSet(TextChangedEvent event) {
     reset();
 }
 /**
  * Called when a traversal key is pressed.
- * Allow tab next traversal to occur when the widget is in single 
- * line mode or in multi line and non-editable mode . 
- * When in editable multi line mode we want to prevent the tab 
+ * Allow tab next traversal to occur when the widget is in single
+ * line mode or in multi line and non-editable mode .
+ * When in editable multi line mode we want to prevent the tab
  * traversal and receive the tab key event instead.
  *
  * @param event the event
@@ -5496,9 +5629,10 @@
                 }
             }
             break;
-    }
-}
-/** 
+        default:
+    }
+}
+/**
  * Scrolls the widget vertically.
  */
 void handleVerticalScroll(Event event) {
@@ -5510,7 +5644,7 @@
  */
 void initializeAccessible() {
     final Accessible accessible = getAccessible();
-    accessible.addAccessibleListener(new AccessibleAdapter() {
+    accessible.addAccessibleListener(new class() AccessibleAdapter {
         public void getName (AccessibleEvent e) {
             String name = null;
             Label label = getAssociatedLabel ();
@@ -5528,26 +5662,26 @@
             if (label !is null) {
                 String text = label.getText ();
                 if (text !is null) {
-                    char mnemonic = _findMnemonic (text);
+                    dchar mnemonic = _findMnemonic (text);
                     if (mnemonic !is '\0') {
-                        shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
+                        shortcut = "Alt+"~tango.text.convert.Utf.toString( [mnemonic] ); //$NON-NLS-1$
                     }
                 }
             }
             e.result = shortcut;
         }
     });
-    accessible.addAccessibleTextListener(new AccessibleTextAdapter() {
+    accessible.addAccessibleTextListener(new class() AccessibleTextAdapter {
         public void getCaretOffset(AccessibleTextEvent e) {
-            e.offset = StyledText.this.getCaretOffset();
+            e.offset = this.outer.getCaretOffset();
         }
         public void getSelectionRange(AccessibleTextEvent e) {
-            Point selection = StyledText.this.getSelectionRange();
+            Point selection = this.outer.getSelectionRange();
             e.offset = selection.x;
             e.length = selection.y;
         }
     });
-    accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
+    accessible.addAccessibleControlListener(new class() AccessibleControlAdapter {
         public void getRole(AccessibleControlEvent e) {
             e.detail = ACC.ROLE_TEXT;
         }
@@ -5560,58 +5694,60 @@
             e.detail = state;
         }
         public void getValue(AccessibleControlEvent e) {
-            e.result = StyledText.this.getText();
-        }
-    }); 
-    addListener(DWT.FocusIn, new Listener() {
+            e.result = this.outer.getText();
+        }
+    });
+    addListener(DWT.FocusIn, new class(accessible) Listener {
+        Accessible acc;
+        this( Accessible acc ){ this.acc = acc; }
         public void handleEvent(Event event) {
-            accessible.setFocus(ACC.CHILDID_SELF);
-        }
-    }); 
-}
-/* 
- * Return the Label immediately preceding the receiver in the z-order, 
- * or null if none. 
+            acc.setFocus(ACC.CHILDID_SELF);
+        }
+    });
+}
+/*
+ * Return the Label immediately preceding the receiver in the z-order,
+ * or null if none.
  */
 Label getAssociatedLabel () {
     Control[] siblings = getParent ().getChildren ();
     for (int i = 0; i < siblings.length; i++) {
-        if (siblings [i] is StyledText.this) {
-            if (i > 0 && siblings [i-1] instanceof Label) {
+        if (siblings [i] is this) {
+            if (i > 0 && ( null !is cast(Label)siblings [i-1])) {
                 return cast(Label) siblings [i-1];
             }
         }
     }
     return null;
 }
-String stripMnemonic (String String) {
+String stripMnemonic (String string) {
     int index = 0;
-    int length = String.length ();
+    int length_ = string.length;
     do {
-        while ((index < length) && (String.charAt (index) !is '&')) index++;
-        if (++index >= length) return String;
-        if (String.charAt (index) !is '&') {
-            return String.substring(0, index-1) + String.substring(index, length);
+        while ((index < length_) && (string[index] !is '&')) index++;
+        if (++index >= length_) return string;
+        if (string[index] !is '&') {
+            return string.substring(0, index-1) ~ string.substring(index, length_);
         }
         index++;
-    } while (index < length);
-    return String;
+    } while (index < length_);
+    return string;
 }
 /*
  * Return the lowercase of the first non-'&' character following
- * an '&' character in the given String. If there are no '&'
- * characters in the given String, return '\0'.
- */
-char _findMnemonic (String String) {
-    if (String is null) return '\0';
+ * an '&' character in the given string. If there are no '&'
+ * characters in the given string, return '\0'.
+ */
+dchar _findMnemonic (String string) {
+    if (string is null) return '\0';
     int index = 0;
-    int length = String.length ();
+    int length_ = string.length;
     do {
-        while (index < length && String.charAt (index) !is '&') index++;
-        if (++index >= length) return '\0';
-        if (String.charAt (index) !is '&') return Character.toLowerCase (String.charAt (index));
+        while (index < length_ && string[index] !is '&') index++;
+        if (++index >= length_) return '\0';
+        if (string[index] !is '&') return CharacterFirstToLower(string[index .. $ ] );
         index++;
-    } while (index < length);
+    } while (index < length_);
     return '\0';
 }
 /**
@@ -5680,7 +5816,7 @@
             doPageEnd();
             clearSelection(true);
             break;
-        // Selection    
+        // Selection
         case ST.SELECT_LINE_UP:
             doSelectionLineUp();
             break;
@@ -5736,7 +5872,7 @@
             doPageEnd();
             doSelection(ST.COLUMN_NEXT);
             break;
-        // Modification         
+        // Modification
         case ST.CUT:
             cut();
             break;
@@ -5762,13 +5898,14 @@
         case ST.TOGGLE_OVERWRITE:
             overwrite = !overwrite;     // toggle insert/overwrite mode
             break;
+        default:
     }
 }
 /**
  * Temporary until DWT provides this
  */
 bool isBidi() {
-    return IS_GTK || IS_CARBON || BidiUtil.isBidiPlatform() || isMirrored;
+    return IS_GTK || IS_CARBON || BidiUtil.isBidiPlatform() || isMirrored_;
 }
 bool isBidiCaret() {
     return BidiUtil.isBidiPlatform();
@@ -5778,45 +5915,45 @@
 }
 /**
  * Returns whether the given offset is inside a multi byte line delimiter.
- * Example: 
+ * Example:
  * "Line1\r\n" isLineDelimiter(5) is false but isLineDelimiter(6) is true
- * 
+ *
  * @return true if the given offset is inside a multi byte line delimiter.
  * false if the given offset is before or after a line delimiter.
  */
 bool isLineDelimiter(int offset) {
     int line = content.getLineAtOffset(offset);
-    int lineOffset = content.getOffsetAtLine(line); 
+    int lineOffset = content.getOffsetAtLine(line);
     int offsetInLine = offset - lineOffset;
-    // offsetInLine will be greater than line length if the line 
+    // offsetInLine will be greater than line length if the line
     // delimiter is longer than one character and the offset is set
     // in between parts of the line delimiter.
-    return offsetInLine > content.getLine(line).length();
-}
-/**
- * Returns whether the widget is mirrored (right oriented/right to left 
- * writing order). 
- * 
- * @return isMirrored true=the widget is right oriented, false=the widget 
+    return offsetInLine > content.getLine(line).length;
+}
+/**
+ * Returns whether the widget is mirrored (right oriented/right to left
+ * writing order).
+ *
+ * @return isMirrored true=the widget is right oriented, false=the widget
  *  is left oriented
  */
 bool isMirrored() {
-    return isMirrored;
+    return isMirrored_;
 }
 /**
  * Returns whether the widget can have only one line.
  *
- * @return true if widget can have only one line, false if widget can have 
+ * @return true if widget can have only one line, false if widget can have
  *  multiple lines
  */
 bool isSingleLine() {
     return (getStyle() & DWT.SINGLE) !is 0;
 }
 /**
- * Sends the specified verify event, replace/insert text as defined by 
+ * Sends the specified verify event, replace/insert text as defined by
  * the event and send a modify event.
  *
- * @param event the text change event. 
+ * @param event the text change event.
  *  <ul>
  *  <li>event.start - the replace start offset</li>
  *  <li>event.end - the replace end offset</li>
@@ -5834,12 +5971,12 @@
         if (isListening(ExtendedModify)) {
             styledTextEvent = new StyledTextEvent(content);
             styledTextEvent.start = event.start;
-            styledTextEvent.end = event.start + event.text.length();
+            styledTextEvent.end = event.start + event.text.length;
             styledTextEvent.text = content.getTextRange(event.start, replacedLength);
         }
         if (updateCaret) {
             //Fix advancing flag for delete/backspace key on direction boundary
-            if (event.text.length() is 0) {
+            if (event.text.length is 0) {
                 int lineIndex = content.getLineAtOffset(event.start);
                 int lineOffset = content.getOffsetAtLine(lineIndex);
                 TextLayout layout = renderer.getTextLayout(lineIndex);
@@ -5864,7 +6001,7 @@
         // fixes 1GBB8NJ
         if (updateCaret) {
             // always update the caret location. fixes 1G8FODP
-            setSelection(event.start + event.text.length(), 0, true);
+            setSelection(event.start + event.text.length, 0, true);
             showCaret();
         }
         sendModifyEvent(event);
@@ -5887,10 +6024,10 @@
         notifyListeners(PaintObject, event);
     }
 }
-/** 
- * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>  
- * clipboard  or, if there is no selection,  inserts the text at the current 
- * caret offset.   If the widget has the DWT.SINGLE style and the 
+/**
+ * Replaces the selection with the text on the <code>DND.CLIPBOARD</code>
+ * clipboard  or, if there is no selection,  inserts the text at the current
+ * caret offset.   If the widget has the DWT.SINGLE style and the
  * clipboard text contains more than one line, only the first line without
  * line delimiters is  inserted in the widget.
  *
@@ -5900,9 +6037,12 @@
  * </ul>
  */
 public void paste(){
-    checkWidget();  
-    String text = cast(String) getClipboardContent(DND.CLIPBOARD);
-    if (text !is null && text.length() > 0) {
+    checkWidget();
+    String text = null;
+    if( auto o = cast(ArrayWrapperString) getClipboardContent(DND.CLIPBOARD)){
+        text = o.array;
+    }
+    if (text !is null && text.length > 0) {
         Event event = new Event();
         event.start = selection.x;
         event.end = selection.y;
@@ -5910,7 +6050,7 @@
         sendKeyEvent(event);
     }
 }
-/** 
+/**
  * Prints the widget's text to the default printer.
  *
  * @exception DWTException <ul>
@@ -5925,18 +6065,21 @@
     options.printTextForeground = true;
     options.printTextBackground = true;
     options.printTextFontStyle = true;
-    options.printLineBackground = true; 
-    new Printing(this, printer, options).run();
+    options.printLineBackground = true;
+    (new Printing(this, printer, options)).run();
     printer.dispose();
 }
-/** 
+/**
  * Returns a runnable that will print the widget's text
  * to the specified printer.
  * <p>
  * The runnable may be run in a non-UI thread.
  * </p>
- * 
+ *
  * @param printer the printer to print to
+ *
+ * @return a <code>Runnable</code> for printing the receiver's text
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -5946,7 +6089,7 @@
  * </ul>
  */
 public Runnable print(Printer printer) {
-    checkWidget();  
+    checkWidget();
     if (printer is null) {
         DWT.error(DWT.ERROR_NULL_ARGUMENT);
     }
@@ -5957,15 +6100,18 @@
     options.printLineBackground = true;
     return print(printer, options);
 }
-/** 
+/**
  * Returns a runnable that will print the widget's text
  * to the specified printer.
  * <p>
  * The runnable may be run in a non-UI thread.
  * </p>
- * 
+ *
  * @param printer the printer to print to
  * @param options print options to use during printing
+ *
+ * @return a <code>Runnable</code> for printing the receiver's text
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -5988,8 +6134,8 @@
  * is processed, the control will be completely painted.
  * <p>
  * Recalculates the content width for all lines in the bounds.
- * When a <code>LineStyleListener</code> is used a redraw call 
- * is the only notification to the widget that styles have changed 
+ * When a <code>LineStyleListener</code> is used a redraw call
+ * is the only notification to the widget that styles have changed
  * and that the content width may have changed.
  * </p>
  *
@@ -6000,7 +6146,7 @@
  *
  * @see Control#update()
  */
-public void redraw() {
+public override void redraw() {
     super.redraw();
     int itemCount = getPartialBottomIndex() - topIndex + 1;
     renderer.reset(topIndex, itemCount);
@@ -6009,18 +6155,18 @@
 }
 /**
  * Causes the rectangular area of the receiver specified by
- * the arguments to be marked as needing to be redrawn. 
+ * the arguments to be marked as needing to be redrawn.
  * The next time a paint request is processed, that area of
  * the receiver will be painted. If the <code>all</code> flag
  * is <code>true</code>, any children of the receiver which
  * intersect with the specified area will also paint their
- * intersecting areas. If the <code>all</code> flag is 
+ * intersecting areas. If the <code>all</code> flag is
  * <code>false</code>, the children will not be painted.
  * <p>
  * Marks the content width of all lines in the specified rectangle
  * as unknown. Recalculates the content width of all visible lines.
- * When a <code>LineStyleListener</code> is used a redraw call 
- * is the only notification to the widget that styles have changed 
+ * When a <code>LineStyleListener</code> is used a redraw call
+ * is the only notification to the widget that styles have changed
  * and that the content width may have changed.
  * </p>
  *
@@ -6037,7 +6183,7 @@
  *
  * @see Control#update()
  */
-public void redraw(int x, int y, int width, int height, bool all) {
+public override void redraw(int x, int y, int width, int height, bool all) {
     super.redraw(x, y, width, height, all);
     if (height > 0) {
         int firstLine = getLineIndex(y);
@@ -6046,7 +6192,7 @@
     }
 }
 void redrawLines(int startLine, int lineCount) {
-    // do nothing if redraw range is completely invisible   
+    // do nothing if redraw range is completely invisible
     int partialBottomIndex = getPartialBottomIndex();
     if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
         return;
@@ -6062,7 +6208,7 @@
     startLine -= topIndex;
     int redrawTop = getLinePixel(startLine);
     int redrawBottom = getLinePixel(startLine + lineCount);
-    int redrawWidth = clientAreaWidth - leftMargin - rightMargin; 
+    int redrawWidth = clientAreaWidth - leftMargin - rightMargin;
     super.redraw(leftMargin, redrawTop, redrawWidth, redrawBottom - redrawTop, true);
 }
 void redrawLinesBullet (int[] redrawLines) {
@@ -6085,7 +6231,7 @@
         super.redraw(0, y, width, height, false);
     }
 }
-/** 
+/**
  * Redraws the specified text range.
  *
  * @param start offset of the first character to redraw
@@ -6093,20 +6239,20 @@
  * @param clearBackground true if the background should be cleared as
  *  part of the redraw operation.  If true, the entire redraw range will
  *  be cleared before anything is redrawn.  If the redraw range includes
- *  the last character of a line (i.e., the entire line is redrawn) the 
+ *  the last character of a line (i.e., the entire line is redrawn) the
  *  line is cleared all the way to the right border of the widget.
- *  The redraw operation will be faster and smoother if clearBackground 
- *  is set to false.  Whether or not the flag can be set to false depends 
- *  on the type of change that has taken place.  If font styles or 
- *  background colors for the redraw range have changed, clearBackground 
- *  should be set to true.  If only foreground colors have changed for 
- *  the redraw range, clearBackground can be set to false. 
+ *  The redraw operation will be faster and smoother if clearBackground
+ *  is set to false.  Whether or not the flag can be set to false depends
+ *  on the type of change that has taken place.  If font styles or
+ *  background colors for the redraw range have changed, clearBackground
+ *  should be set to true.  If only foreground colors have changed for
+ *  the redraw range, clearBackground can be set to false.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li> 
+ *   <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
  * </ul>
  */
 public void redrawRange(int start, int length, bool clearBackground) {
@@ -6124,7 +6270,8 @@
 /**
  * Removes the specified bidirectional segment listener.
  *
- * @param listener the listener
+ * @param listener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6132,17 +6279,19 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
  * </ul>
+ *
  * @since 2.0
  */
 public void removeBidiSegmentListener(BidiSegmentListener listener) {
     checkWidget();
     if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    removeListener(LineGetSegments, listener);  
+    removeListener(LineGetSegments, listener);
 }
 /**
  * Removes the specified extended modify listener.
  *
- * @param extendedModifyListener the listener
+ * @param extendedModifyListener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6154,12 +6303,13 @@
 public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
     checkWidget();
     if (extendedModifyListener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    removeListener(ExtendedModify, extendedModifyListener); 
+    removeListener(ExtendedModify, extendedModifyListener);
 }
 /**
  * Removes the specified line background listener.
  *
- * @param listener the listener
+ * @param listener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6176,7 +6326,8 @@
 /**
  * Removes the specified line style listener.
  *
- * @param listener the listener
+ * @param listener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6193,7 +6344,8 @@
 /**
  * Removes the specified modify listener.
  *
- * @param modifyListener the listener
+ * @param modifyListener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6210,7 +6362,8 @@
 /**
  * Removes the specified listener.
  *
- * @param listener the listener
+ * @param listener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6250,7 +6403,8 @@
 /**
  * Removes the specified verify listener.
  *
- * @param verifyListener the listener
+ * @param verifyListener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6267,7 +6421,8 @@
 /**
  * Removes the specified key verify listener.
  *
- * @param listener the listener
+ * @param listener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6283,7 +6438,8 @@
 /**
  * Removes the specified word movement listener.
  *
- * @param listener the listener
+ * @param listener the listener which should no longer be notified
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -6291,24 +6447,24 @@
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_NULL_ARGUMENT when listener is null</li>
  * </ul>
- * 
+ *
  * @see MovementEvent
  * @see MovementListener
  * @see #addWordMovementListener
- * 
+ *
  * @since 3.3
  */
 
 public void removeWordMovementListener(MovementListener listener) {
     checkWidget();
     if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    removeListener(WordNext, listener); 
+    removeListener(WordNext, listener);
     removeListener(WordPrevious, listener);
 }
-/** 
+/**
  * Replaces the styles in the given range with new styles.  This method
  * effectively deletes the styles in the given range and then adds the
- * the new styles. 
+ * the new styles.
  * <p>
  * Note: Because a StyleRange includes the start and length, the
  * same instance cannot occur multiple times in the array of styles.
@@ -6316,14 +6472,14 @@
  * multiple StyleRanges, <code>setStyleRanges(int, int, int[], StyleRange[])</code>
  * can be used to share styles and reduce memory usage.
  * </p><p>
- * Should not be called if a LineStyleListener has been set since the 
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p>
  *
  * @param start offset of first character where styles will be deleted
  * @param length length of the range to delete styles in
  * @param ranges StyleRange objects containing the new style information.
- * The ranges should not overlap and should be within the specified start 
+ * The ranges should not overlap and should be within the specified start
  * and length. The style rendering is undefined if the ranges do overlap
  * or are ill-defined. Must not be null.
  * @exception DWTException <ul>
@@ -6331,33 +6487,33 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> 
- *   <li>ERROR_NULL_ARGUMENT when ranges is null</li>
- * </ul>
- * 
+ *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
+ * </ul>
+ *
  * @since 2.0
- * 
+ *
  * @see #setStyleRanges(int, int, int[], StyleRange[])
  */
 public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
     checkWidget();
     if (isListening(LineGetStyle)) return;
-    if (ranges is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    // DWT extension: allow null for zero length string
+    //if (ranges is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     setStyleRanges(start, length, null, ranges, false);
 }
 /**
  * Replaces the given text range with new text.
- * If the widget has the DWT.SINGLE style and "text" contains more than 
- * one line, only the first line is rendered but the text is stored 
- * unchanged. A subsequent call to getText will return the same text 
- * that was set. Note that only a single line of text should be set when 
+ * If the widget has the DWT.SINGLE style and "text" contains more than
+ * one line, only the first line is rendered but the text is stored
+ * unchanged. A subsequent call to getText will return the same text
+ * that was set. Note that only a single line of text should be set when
  * the DWT.SINGLE style is used.
  * <p>
  * <b>NOTE:</b> During the replace operation the current selection is
  * changed as follows:
- * <ul> 
+ * <ul>
  * <li>selection before replaced text: selection unchanged
- * <li>selection after replaced text: adjust the selection so that same text 
+ * <li>selection after replaced text: adjust the selection so that same text
  * remains selected
  * <li>selection intersects replaced text: selection is cleared and caret
  * is placed after inserted text
@@ -6372,17 +6528,17 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li> 
- *   <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter. 
- *      Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li>  
- *   <li>ERROR_NULL_ARGUMENT when String is null</li>
+ *   <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
+ *   <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
+ *      Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li>
  * </ul>
  */
 public void replaceTextRange(int start, int length, String text) {
     checkWidget();
-    if (text is null) {
-        DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    }
+    // DWT extension: allow null for zero length string
+//     if (text is null) {
+//         DWT.error(DWT.ERROR_NULL_ARGUMENT);
+//     }
     int contentLength = getCharCount();
     int end = start + length;
     if (start > end || start < 0 || end > contentLength) {
@@ -6405,14 +6561,14 @@
     topIndex = 0;
     topIndexY = 0;
     verticalScrollOffset = 0;
-    horizontalScrollOffset = 0; 
+    horizontalScrollOffset = 0;
     resetSelection();
     renderer.setContent(content);
     if (verticalBar !is null) {
         verticalBar.setSelection(0);
     }
     if (horizontalBar !is null) {
-        horizontalBar.setSelection(0);  
+        horizontalBar.setSelection(0);
     }
     resetCache(0, 0);
     setCaretLocation();
@@ -6441,7 +6597,7 @@
     selectionAnchor = -1;
 }
 
-public void scroll(int destX, int destY, int x, int y, int width, int height, bool all) {
+public override void scroll(int destX, int destY, int x, int y, int width, int height, bool all) {
     super.scroll(destX, destY, x, y, width, height, false);
     if (all) {
         int deltaX = destX - x, deltaY = destY - y;
@@ -6459,11 +6615,11 @@
  *
  * @param pixels number of pixels to scroll, > 0 = scroll left,
  *  < 0 scroll right
- * @param adjustScrollBar 
+ * @param adjustScrollBar
  *  true= the scroll thumb will be moved to reflect the new scroll offset.
  *  false = the scroll thumb will not be moved
- * @return 
- *  true=the widget was scrolled 
+ * @return
+ *  true=the widget was scrolled
  *  false=the widget was not scrolled, the given offset is not valid.
  */
 bool scrollHorizontal(int pixels, bool adjustScrollBar) {
@@ -6502,11 +6658,11 @@
  * Scrolls the widget vertically.
  *
  * @param pixel the new vertical scroll offset
- * @param adjustScrollBar 
+ * @param adjustScrollBar
  *  true= the scroll thumb will be moved to reflect the new scroll offset.
  *  false = the scroll thumb will not be moved
- * @return 
- *  true=the widget was scrolled 
+ * @return
+ *  true=the widget was scrolled
  *  false=the widget was not scrolled
  */
 bool scrollVertical(int pixels, bool adjustScrollBar) {
@@ -6574,7 +6730,7 @@
         super.redraw(leftMargin, clientAreaHeight - bottomMargin, scrollWidth, bottomMargin, false);
     }
 }
-/** 
+/**
  * Selects all the text.
  *
  * @exception DWTException <ul>
@@ -6589,7 +6745,7 @@
 /**
  * Replaces/inserts text as defined by the event.
  *
- * @param event the text change event. 
+ * @param event the text change event.
  *  <ul>
  *  <li>event.start - the replace start offset</li>
  *  <li>event.end - the replace end offset</li>
@@ -6602,19 +6758,19 @@
     }
 }
 /**
- * Returns a StyledTextEvent that can be used to request data such 
+ * Returns a StyledTextEvent that can be used to request data such
  * as styles and background color for a line.
  * <p>
- * The specified line may be a visual (wrapped) line if in word 
- * wrap mode. The returned object will always be for a logical 
+ * The specified line may be a visual (wrapped) line if in word
+ * wrap mode. The returned object will always be for a logical
  * (unwrapped) line.
  * </p>
  *
  * @param lineOffset offset of the line. This may be the offset of
  *  a visual line if the widget is in word wrap mode.
- * @param line line text. This may be the text of a visual line if 
+ * @param line line text. This may be the text of a visual line if
  *  the widget is in word wrap mode.
- * @return StyledTextEvent that can be used to request line data 
+ * @return StyledTextEvent that can be used to request line data
  *  for the given line.
  */
 StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
@@ -6632,14 +6788,14 @@
 }
 void sendModifyEvent(Event event) {
     Accessible accessible = getAccessible();
-    if (event.text.length() is 0) {
+    if (event.text.length is 0) {
         accessible.textChanged(ACC.TEXT_DELETE, event.start, event.end - event.start);
     } else {
         if (event.start is event.end) {
-            accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length());
+            accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length);
         } else {
             accessible.textChanged(ACC.TEXT_DELETE, event.start, event.end - event.start);
-            accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length());  
+            accessible.textChanged(ACC.TEXT_INSERT, event.start, event.text.length);
         }
     }
     notifyListeners(DWT.Modify, event);
@@ -6681,22 +6837,22 @@
     return newOffset;
 }
 /**
- * Sets the alignment of the widget. The argument should be one of <code>DWT.LEFT</code>, 
+ * Sets the alignment of the widget. The argument should be one of <code>DWT.LEFT</code>,
  * <code>DWT.CENTER</code> or <code>DWT.RIGHT</code>. The alignment applies for all lines.
  * </p><p>
  * Note that if <code>DWT.MULTI</code> is set, then <code>DWT.WRAP</code> must also be set
  * in order to stabilize the right edge before setting alignment.
  * </p>
- * 
+ *
  * @param alignment the new alignment
- *  
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @see #setLineAlignment(int, int, int)
- *  
+ *
  * @since 3.2
  */
 public void setAlignment(int alignment) {
@@ -6711,7 +6867,7 @@
 /**
  * @see Control#setBackground(Color)
  */
-public void setBackground(Color color) {
+public override void setBackground(Color color) {
     checkWidget();
     background = color;
     super.setBackground(color);
@@ -6719,7 +6875,7 @@
 }
 /**
  * Sets the receiver's caret.  Set the caret's height and location.
- * 
+ *
  * </p>
  * @param caret the new caret for the receiver
  *
@@ -6728,7 +6884,7 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  */
-public void setCaret(Caret caret) {
+public override void setCaret(Caret caret) {
     checkWidget ();
     super.setCaret(caret);
     caretDirection = DWT.NULL;
@@ -6746,7 +6902,7 @@
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * 
+ *
  * @deprecated use BidiSegmentListener instead.
  */
 public void setBidiColoring(bool mode) {
@@ -6818,7 +6974,7 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
+ *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
  * </ul>
  */
@@ -6832,7 +6988,7 @@
             caretOffset = length;
         } else {
             if (isLineDelimiter(offset)) {
-                // offset is inside a multi byte line delimiter. This is an 
+                // offset is inside a multi byte line delimiter. This is an
                 // illegal operation and an exception is thrown. Fixes 1GDKK3R
                 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
             }
@@ -6844,14 +7000,14 @@
         clearSelection(false);
     }
     setCaretLocation();
-}   
+}
 /**
  * Copies the specified text range to the clipboard.  The text will be placed
  * in the clipboard in plain text format and RTF format.
  *
  * @param start start index of the text
  * @param length length of text to place in clipboard
- * 
+ *
  * @exception DWTError, see Clipboard.setContents
  * @see dwt.dnd.Clipboard#setContents
  */
@@ -6863,14 +7019,14 @@
     Object[] data;
     Transfer[] types;
     if (clipboardType is DND.SELECTION_CLIPBOARD) {
-        data = new Object[]{plainText};
-        types = new Transfer[]{plainTextTransfer};
+        data = [ cast(Object) new ArrayWrapperString(plainText) ];
+        types = [plainTextTransfer];
     } else {
         RTFTransfer rtfTransfer = RTFTransfer.getInstance();
         RTFWriter rtfWriter = new RTFWriter(start, length);
         String rtfText = getPlatformDelimitedText(rtfWriter);
-        data = new Object[]{rtfText, plainText};
-        types = new Transfer[]{rtfTransfer, plainTextTransfer};
+        data = [ cast(Object) new ArrayWrapperString(rtfText), new ArrayWrapperString(plainText) ];
+        types = [ cast(Transfer)rtfTransfer, plainTextTransfer];
     }
     clipboard.setContents(data, types, clipboardType);
 }
@@ -6887,7 +7043,7 @@
  * </ul>
  */
 public void setContent(StyledTextContent newContent) {
-    checkWidget();  
+    checkWidget();
     if (newContent is null) {
         DWT.error(DWT.ERROR_NULL_ARGUMENT);
     }
@@ -6900,12 +7056,12 @@
 }
 /**
  * Sets the receiver's cursor to the cursor specified by the
- * argument.  Overridden to handle the null case since the 
+ * argument.  Overridden to handle the null case since the
  * StyledText widget uses an ibeam as its default cursor.
  *
  * @see Control#setCursor(Cursor)
  */
-public void setCursor (Cursor cursor) {
+public override void setCursor (Cursor cursor) {
     if (cursor is null) {
         Display display = getDisplay();
         super.setCursor(display.getSystemCursor(DWT.CURSOR_IBEAM));
@@ -6913,7 +7069,7 @@
         super.setCursor(cursor);
     }
 }
-/** 
+/**
  * Sets whether the widget : double click mouse behavior.
  * </p>
  *
@@ -6928,15 +7084,15 @@
     checkWidget();
     doubleClickEnabled = enable;
 }
-public void setDragDetect (bool dragDetect) {
+public override void setDragDetect (bool dragDetect_) {
     checkWidget ();
-    this.dragDetect = dragDetect;
+    this.dragDetect_ = dragDetect_;
 }
 /**
  * Sets whether the widget content can be edited.
  * </p>
  *
- * @param editable if true content can be edited, if false content can not be 
+ * @param editable if true content can be edited, if false content can not be
  *  edited
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -6960,10 +7116,10 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  */
-public void setFont(Font font) {
+public override void setFont(Font font) {
     checkWidget();
     int oldLineHeight = renderer.getLineHeight();
-    super.setFont(font);    
+    super.setFont(font);
     renderer.setFont(getFont(), tabLength);
     // keep the same top line visible. fixes 5815
     if (isFixedLineHeight()) {
@@ -6974,32 +7130,29 @@
         }
     }
     resetCache(0, content.getLineCount());
-    claimBottomFreeSpace(); 
+    claimBottomFreeSpace();
     calculateScrollBars();
     if (isBidiCaret()) createCaretBitmaps();
     caretDirection = DWT.NULL;
     setCaretLocation();
     super.redraw();
 }
-/**
- * @see dwt.widgets.Control#setForeground
- */
-public void setForeground(Color color) {
+public override void setForeground(Color color) {
     checkWidget();
     foreground = color;
     super.setForeground(getForeground());
     super.redraw();
 }
-/** 
+/**
  * Sets the horizontal scroll offset relative to the start of the line.
  * Do nothing if there is no text set.
  * <p>
- * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the 
+ * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
  * widget.
  * </p>
  *
- * @param offset horizontal scroll offset relative to the start 
- *  of the line, measured in character increments starting at 0, if 
+ * @param offset horizontal scroll offset relative to the start
+ *  of the line, measured in character increments starting at 0, if
  *  equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -7010,14 +7163,14 @@
     checkWidget();
     if (getCharCount() is 0) {
         return;
-    }   
+    }
     if (offset < 0) {
         offset = 0;
     }
     offset *= getHorizontalIncrement();
-    // allow any value if client area width is unknown or 0. 
+    // allow any value if client area width is unknown or 0.
     // offset will be checked in resize handler.
-    // don't use isVisible since width is known even if widget 
+    // don't use isVisible since width is known even if widget
     // is temporarily invisible
     if (clientAreaWidth > 0) {
         int width = renderer.getWidth();
@@ -7030,15 +7183,15 @@
     }
     scrollHorizontal(offset - horizontalScrollOffset, true);
 }
-/** 
+/**
  * Sets the horizontal pixel offset relative to the start of the line.
  * Do nothing if there is no text set.
  * <p>
- * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text 
+ * <b>NOTE:</b> The horizontal pixel offset is reset to 0 when new text
  * is set in the widget.
  * </p>
  *
- * @param pixel horizontal pixel offset relative to the start 
+ * @param pixel horizontal pixel offset relative to the start
  *  of the line.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -7050,13 +7203,13 @@
     checkWidget();
     if (getCharCount() is 0) {
         return;
-    }   
+    }
     if (pixel < 0) {
         pixel = 0;
     }
-    // allow any value if client area width is unknown or 0. 
+    // allow any value if client area width is unknown or 0.
     // offset will be checked in resize handler.
-    // don't use isVisible since width is known even if widget 
+    // don't use isVisible since width is known even if widget
     // is temporarily invisible
     if (clientAreaWidth > 0) {
         int width = renderer.getWidth();
@@ -7072,19 +7225,19 @@
 /**
  * Sets the line indentation of the widget.
  * <p>
- * It is the amount of blank space, in pixels, at the beginning of each line. 
- * When a line wraps in several lines only the first one is indented. 
+ * It is the amount of blank space, in pixels, at the beginning of each line.
+ * When a line wraps in several lines only the first one is indented.
  * </p>
- * 
+ *
  * @param indent the new indent
- *  
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @see #setLineIndent(int, int, int)
- *  
+ *
  * @since 3.2
  */
 public void setIndent(int indent) {
@@ -7093,20 +7246,20 @@
     this.indent = indent;
     resetCache(0, content.getLineCount());
     setCaretLocation();
-    super.redraw(); 
-}
-/**
- * Sets whether the widget should justify lines.  
- * 
+    super.redraw();
+}
+/**
+ * Sets whether the widget should justify lines.
+ *
  * @param justify whether lines should be justified
- *  
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @see #setLineJustify(int, int, bool)
- *  
+ *
  * @since 3.2
  */
 public void setJustify(bool justify) {
@@ -7115,21 +7268,21 @@
     this.justify = justify;
     resetCache(0, content.getLineCount());
     setCaretLocation();
-    super.redraw(); 
-}
-/** 
+    super.redraw();
+}
+/**
  * Maps a key to an action.
  * <p>
- * One action can be associated with N keys. However, each key can only 
+ * One action can be associated with N keys. However, each key can only
  * have one action (key:action is N:1 relation).
  * </p>
  *
- * @param key a key code defined in DWT.java or a character. 
+ * @param key a key code defined in DWT.java or a character.
  *  Optionally ORd with a state mask.  Preferred state masks are one or more of
- *  DWT.MOD1, DWT.MOD2, DWT.MOD3, since these masks account for modifier platform 
+ *  DWT.MOD1, DWT.MOD2, DWT.MOD3, since these masks account for modifier platform
  *  differences.  However, there may be cases where using the specific state masks
  *  (i.e., DWT.CTRL, DWT.SHIFT, DWT.ALT, DWT.COMMAND) makes sense.
- * @param action one of the predefined actions defined in ST.java. 
+ * @param action one of the predefined actions defined in ST.java.
  *  Use DWT.NULL to remove a key binding.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -7139,58 +7292,58 @@
 public void setKeyBinding(int key, int action) {
     checkWidget();
     int modifierValue = key & DWT.MODIFIER_MASK;
-    char keyChar = cast(wchar)(key & DWT.KEY_MASK);
+    char keyChar = cast(char)(key & DWT.KEY_MASK);
     if (Compatibility.isLetter(keyChar)) {
         // make the keybinding case insensitive by adding it
         // in its upper and lower case form
-        char ch = Character.toUpperCase(keyChar);
+        char ch = CharacterToUpper(keyChar);
         int newKey = ch | modifierValue;
         if (action is DWT.NULL) {
-            keyActionMap.remove(new Integer(newKey));
+            keyActionMap.remove(newKey);
         } else {
-            keyActionMap.put(new Integer(newKey), new Integer(action));
-        }
-        ch = Character.toLowerCase(keyChar);
+            keyActionMap[newKey] = action;
+        }
+        ch = CharacterToLower(keyChar);
         newKey = ch | modifierValue;
         if (action is DWT.NULL) {
-            keyActionMap.remove(new Integer(newKey));
+            keyActionMap.remove(newKey);
         } else {
-            keyActionMap.put(new Integer(newKey), new Integer(action));
+            keyActionMap[newKey] = action;
         }
     } else {
         if (action is DWT.NULL) {
-            keyActionMap.remove(new Integer(key));
+            keyActionMap.remove(key);
         } else {
-            keyActionMap.put(new Integer(key), new Integer(action));
-        }
-    }       
-}
-/**
- * Sets the alignment of the specified lines. The argument should be one of <code>DWT.LEFT</code>, 
+            keyActionMap[key]=action;
+        }
+    }
+}
+/**
+ * Sets the alignment of the specified lines. The argument should be one of <code>DWT.LEFT</code>,
  * <code>DWT.CENTER</code> or <code>DWT.RIGHT</code>.
  * <p><p>
  * Note that if <code>DWT.MULTI</code> is set, then <code>DWT.WRAP</code> must also be set
  * in order to stabilize the right edge before setting alignment.
  * </p>
- * Should not be called if a LineStyleListener has been set since the listener 
+ * Should not be called if a LineStyleListener has been set since the listener
  * maintains the line attributes.
  * </p><p>
- * All line attributes are maintained relative to the line text, not the 
+ * All line attributes are maintained relative to the line text, not the
  * line index that is specified in this method call.
- * During text changes, when entire lines are inserted or removed, the line 
- * attributes that are associated with the lines after the change 
- * will "move" with their respective text. An entire line is defined as 
- * extending from the first character on a line to the last and including the 
- * line delimiter. 
+ * During text changes, when entire lines are inserted or removed, the line
+ * attributes that are associated with the lines after the change
+ * will "move" with their respective text. An entire line is defined as
+ * extending from the first character on a line to the last and including the
+ * line delimiter.
  * </p><p>
- * When two lines are joined by deleting a line delimiter, the top line 
- * attributes take precedence and the attributes of the bottom line are deleted. 
- * For all other text changes line attributes will remain unchanged. 
- *   
+ * When two lines are joined by deleting a line delimiter, the top line
+ * attributes take precedence and the attributes of the bottom line are deleted.
+ * For all other text changes line attributes will remain unchanged.
+ *
  * @param startLine first line the alignment is applied to, 0 based
  * @param lineCount number of lines the alignment applies to.
  * @param alignment line alignment
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -7216,30 +7369,30 @@
         setCaretLocation();
     }
 }
-/** 
+/**
  * Sets the background color of the specified lines.
  * <p>
  * The background color is drawn for the width of the widget. All
  * line background colors are discarded when setText is called.
- * The text background color if defined in a StyleRange overlays the 
- * line background color. 
+ * The text background color if defined in a StyleRange overlays the
+ * line background color.
  * </p><p>
- * Should not be called if a LineBackgroundListener has been set since the 
+ * Should not be called if a LineBackgroundListener has been set since the
  * listener maintains the line backgrounds.
  * </p><p>
- * All line attributes are maintained relative to the line text, not the 
+ * All line attributes are maintained relative to the line text, not the
  * line index that is specified in this method call.
- * During text changes, when entire lines are inserted or removed, the line 
- * attributes that are associated with the lines after the change 
- * will "move" with their respective text. An entire line is defined as 
- * extending from the first character on a line to the last and including the 
- * line delimiter. 
+ * During text changes, when entire lines are inserted or removed, the line
+ * attributes that are associated with the lines after the change
+ * will "move" with their respective text. An entire line is defined as
+ * extending from the first character on a line to the last and including the
+ * line delimiter.
  * </p><p>
- * When two lines are joined by deleting a line delimiter, the top line 
- * attributes take precedence and the attributes of the bottom line are deleted. 
- * For all other text changes line attributes will remain unchanged. 
+ * When two lines are joined by deleting a line delimiter, the top line
+ * attributes take precedence and the attributes of the bottom line are deleted.
+ * For all other text changes line attributes will remain unchanged.
  * </p>
- * 
+ *
  * @param startLine first line the color is applied to, 0 based
  * @param lineCount number of lines the color applies to.
  * @param background line background color
@@ -7252,7 +7405,7 @@
  * </ul>
  */
 public void setLineBackground(int startLine, int lineCount, Color background) {
-    checkWidget();  
+    checkWidget();
     if (isListening(LineGetBackground)) return;
     if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
         DWT.error(DWT.ERROR_INVALID_ARGUMENT);
@@ -7267,26 +7420,26 @@
 /**
  * Sets the bullet of the specified lines.
  * <p>
- * Should not be called if a LineStyleListener has been set since the listener 
+ * Should not be called if a LineStyleListener has been set since the listener
  * maintains the line attributes.
  * </p><p>
- * All line attributes are maintained relative to the line text, not the 
+ * All line attributes are maintained relative to the line text, not the
  * line index that is specified in this method call.
- * During text changes, when entire lines are inserted or removed, the line 
- * attributes that are associated with the lines after the change 
- * will "move" with their respective text. An entire line is defined as 
- * extending from the first character on a line to the last and including the 
- * line delimiter. 
+ * During text changes, when entire lines are inserted or removed, the line
+ * attributes that are associated with the lines after the change
+ * will "move" with their respective text. An entire line is defined as
+ * extending from the first character on a line to the last and including the
+ * line delimiter.
  * </p><p>
- * When two lines are joined by deleting a line delimiter, the top line 
- * attributes take precedence and the attributes of the bottom line are deleted. 
+ * When two lines are joined by deleting a line delimiter, the top line
+ * attributes take precedence and the attributes of the bottom line are deleted.
  * For all other text changes line attributes will remain unchanged.
  * </p>
  *
  * @param startLine first line the bullet is applied to, 0 based
  * @param lineCount number of lines the bullet applies to.
  * @param bullet line bullet
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -7298,7 +7451,7 @@
  */
 public void setLineBullet(int startLine, int lineCount, Bullet bullet) {
     checkWidget();
-    if (isListening(LineGetStyle)) return;  
+    if (isListening(LineGetStyle)) return;
     if (startLine < 0 || startLine + lineCount > content.getLineCount()) {
         DWT.error(DWT.ERROR_INVALID_ARGUMENT);
     }
@@ -7319,26 +7472,26 @@
 /**
  * Sets the indent of the specified lines.
  * <p>
- * Should not be called if a LineStyleListener has been set since the listener 
+ * Should not be called if a LineStyleListener has been set since the listener
  * maintains the line attributes.
  * </p><p>
- * All line attributes are maintained relative to the line text, not the 
+ * All line attributes are maintained relative to the line text, not the
  * line index that is specified in this method call.
- * During text changes, when entire lines are inserted or removed, the line 
- * attributes that are associated with the lines after the change 
- * will "move" with their respective text. An entire line is defined as 
- * extending from the first character on a line to the last and including the 
- * line delimiter. 
+ * During text changes, when entire lines are inserted or removed, the line
+ * attributes that are associated with the lines after the change
+ * will "move" with their respective text. An entire line is defined as
+ * extending from the first character on a line to the last and including the
+ * line delimiter.
  * </p><p>
- * When two lines are joined by deleting a line delimiter, the top line 
- * attributes take precedence and the attributes of the bottom line are deleted. 
+ * When two lines are joined by deleting a line delimiter, the top line
+ * attributes take precedence and the attributes of the bottom line are deleted.
  * For all other text changes line attributes will remain unchanged.
  * </p>
  *
  * @param startLine first line the indent is applied to, 0 based
  * @param lineCount number of lines the indent applies to.
  * @param indent line indent
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -7367,26 +7520,26 @@
 /**
  * Sets the justify of the specified lines.
  * <p>
- * Should not be called if a LineStyleListener has been set since the listener 
+ * Should not be called if a LineStyleListener has been set since the listener
  * maintains the line attributes.
  * </p><p>
- * All line attributes are maintained relative to the line text, not the 
+ * All line attributes are maintained relative to the line text, not the
  * line index that is specified in this method call.
- * During text changes, when entire lines are inserted or removed, the line 
- * attributes that are associated with the lines after the change 
- * will "move" with their respective text. An entire line is defined as 
- * extending from the first character on a line to the last and including the 
- * line delimiter. 
+ * During text changes, when entire lines are inserted or removed, the line
+ * attributes that are associated with the lines after the change
+ * will "move" with their respective text. An entire line is defined as
+ * extending from the first character on a line to the last and including the
+ * line delimiter.
  * </p><p>
- * When two lines are joined by deleting a line delimiter, the top line 
- * attributes take precedence and the attributes of the bottom line are deleted. 
+ * When two lines are joined by deleting a line delimiter, the top line
+ * attributes take precedence and the attributes of the bottom line are deleted.
  * For all other text changes line attributes will remain unchanged.
  * </p>
- *  
+ *
  * @param startLine first line the justify is applied to, 0 based
  * @param lineCount number of lines the justify applies to.
  * @param justify true if lines should be justified
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
@@ -7414,7 +7567,7 @@
 }
 /**
  * Sets the line spacing of the widget. The line spacing applies for all lines.
- * 
+ *
  * @param lineSpacing the line spacing
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -7425,7 +7578,7 @@
 public void setLineSpacing(int lineSpacing) {
     checkWidget();
     if (this.lineSpacing is lineSpacing || lineSpacing < 0) return;
-    this.lineSpacing = lineSpacing; 
+    this.lineSpacing = lineSpacing;
     setVariableLineHeight();
     resetCache(0, content.getLineCount());
     setCaretLocation();
@@ -7456,43 +7609,43 @@
  * of the constants <code>DWT.LEFT_TO_RIGHT</code> or <code>DWT.RIGHT_TO_LEFT</code>.
  *
  * @param orientation new orientation style
- * 
- * @exception DWTException <ul>
- *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * 
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
  * @since 2.1.2
  */
 public void setOrientation(int orientation) {
-    if ((orientation & (DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT)) is 0) { 
+    if ((orientation & (DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT)) is 0) {
         return;
     }
     if ((orientation & DWT.RIGHT_TO_LEFT) !is 0 && (orientation & DWT.LEFT_TO_RIGHT) !is 0) {
-        return; 
+        return;
     }
     if ((orientation & DWT.RIGHT_TO_LEFT) !is 0 && isMirrored()) {
-        return; 
-    } 
+        return;
+    }
     if ((orientation & DWT.LEFT_TO_RIGHT) !is 0 && !isMirrored()) {
         return;
     }
     if (!BidiUtil.setOrientation(this, orientation)) {
         return;
     }
-    isMirrored = (orientation & DWT.RIGHT_TO_LEFT) !is 0;
+    isMirrored_ = (orientation & DWT.RIGHT_TO_LEFT) !is 0;
     caretDirection = DWT.NULL;
     resetCache(0, content.getLineCount());
     setCaretLocation();
-    keyActionMap.clear();
+    keyActionMap = null;
     createKeyBindings();
     super.redraw();
 }
 /**
- * Adjusts the maximum and the page size of the scroll bars to 
+ * Adjusts the maximum and the page size of the scroll bars to
  * reflect content width/length changes.
- * 
- * @param vertical indicates if the vertical scrollbar also needs to be set 
+ *
+ * @param vertical indicates if the vertical scrollbar also needs to be set
  */
 void setScrollBars(bool vertical) {
     int inactive = 1;
@@ -7500,7 +7653,7 @@
         ScrollBar verticalBar = getVerticalBar();
         if (verticalBar !is null) {
             int maximum = renderer.getHeight();
-            // only set the real values if the scroll bar can be used 
+            // only set the real values if the scroll bar can be used
             // (ie. because the thumb size is less than the scroll maximum)
             // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
             if (clientAreaHeight < maximum) {
@@ -7521,7 +7674,7 @@
     ScrollBar horizontalBar = getHorizontalBar();
     if (horizontalBar !is null && horizontalBar.getVisible()) {
         int maximum = renderer.getWidth();
-        // only set the real values if the scroll bar can be used 
+        // only set the real values if the scroll bar can be used
         // (ie. because the thumb size is less than the scroll maximum)
         // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
         if (clientAreaWidth < maximum) {
@@ -7539,7 +7692,7 @@
         }
     }
 }
-/** 
+/**
  * Sets the selection to the given position and scrolls it into view.  Equivalent to setSelection(start,start).
  *
  * @param start new caret position
@@ -7549,19 +7702,19 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
+ *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul> 
+ * </ul>
  */
 public void setSelection(int start) {
-    // checkWidget test done in setSelectionRange   
+    // checkWidget test done in setSelectionRange
     setSelection(start, start);
 }
-/** 
+/**
  * Sets the selection and scrolls it into view.
  * <p>
  * Indexing is zero based.  Text selections are specified in terms of
- * caret positions.  In a text widget that contains N characters, there are 
+ * caret positions.  In a text widget that contains N characters, there are
  * N+1 caret positions, ranging from 0..N
  * </p>
  *
@@ -7574,13 +7727,13 @@
  * </ul>
  * @exception IllegalArgumentException <ul>
  *   <li>ERROR_NULL_ARGUMENT when point is null</li>
- *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
+ *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul> 
+ * </ul>
  */
 public void setSelection(Point point) {
     checkWidget();
-    if (point is null) DWT.error (DWT.ERROR_NULL_ARGUMENT); 
+    if (point is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
     setSelection(point.x, point.y);
 }
 /**
@@ -7591,7 +7744,7 @@
  * @param color the new color (or null)
  *
  * @exception IllegalArgumentException <ul>
- *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
  * </ul>
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -7610,7 +7763,7 @@
 /**
  * Sets the receiver's selection foreground color to the color specified
  * by the argument, or to the default system color for the control
- * if the argument is null. 
+ * if the argument is null.
  * <p>
  * Note that this is a <em>HINT</em>. Some platforms do not allow the application
  * to change the selection foreground color.
@@ -7618,7 +7771,7 @@
  * @param color the new color (or null)
  *
  * @exception IllegalArgumentException <ul>
- *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
  * </ul>
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -7634,15 +7787,15 @@
     selectionForeground = color;
     super.redraw();
 }
-/** 
+/**
  * Sets the selection and scrolls it into view.
  * <p>
  * Indexing is zero based.  Text selections are specified in terms of
- * caret positions.  In a text widget that contains N characters, there are 
+ * caret positions.  In a text widget that contains N characters, there are
  * N+1 caret positions, ranging from 0..N
  * </p>
  *
- * @param start selection start offset. The caret will be placed at the 
+ * @param start selection start offset. The caret will be placed at the
  *  selection start when start > end.
  * @param end selection end offset
  * @see #setSelectionRange(int,int)
@@ -7651,7 +7804,7 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
+ *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
  * </ul>
  */
@@ -7659,31 +7812,33 @@
     setSelectionRange(start, end - start);
     showSelection();
 }
-/** 
+/**
  * Sets the selection.
  * <p>
- * The new selection may not be visible. Call showSelection to scroll 
+ * The new selection may not be visible. Call showSelection to scroll
  * the selection into view.
  * </p>
  *
  * @param start offset of the first selected character, start >= 0 must be true.
- * @param length number of characters to select, 0 <= start + length 
- *  <= getCharCount() must be true. 
+ * @param length number of characters to select, 0 <= start + length
+ *  <= getCharCount() must be true.
  *  A negative length places the caret at the selection start.
- * @param sendEvent a Selection event is sent when set to true and when 
+ * @param sendEvent a Selection event is sent when set to true and when
  *  the selection is reset.
  */
 void setSelection(int start, int length, bool sendEvent) {
     int end = start + length;
+    start = content.utf8AdjustOffset(start);
+    end = content.utf8AdjustOffset(end);
     if (start > end) {
         int temp = end;
         end = start;
         start = temp;
     }
-    // is the selection range different or is the selection direction 
+    // is the selection range different or is the selection direction
     // different?
-    if (selection.x !is start || selection.y !is end || 
-        (length > 0 && selectionAnchor !is selection.x) || 
+    if (selection.x !is start || selection.y !is end ||
+        (length > 0 && selectionAnchor !is selection.x) ||
         (length < 0 && selectionAnchor !is selection.y)) {
         clearSelection(sendEvent);
         if (length < 0) {
@@ -7697,7 +7852,7 @@
         internalRedrawRange(selection.x, selection.y - selection.x);
     }
 }
-/** 
+/**
  * Sets the selection.
  * <p>
  * The new selection may not be visible. Call showSelection to scroll the selection
@@ -7706,13 +7861,13 @@
  *
  * @param start offset of the first selected character
  * @param length number of characters to select
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a 
+ *   <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
  * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
  * </ul>
  */
@@ -7727,23 +7882,23 @@
         if (end > contentLength) length = contentLength - start;
     }
     if (isLineDelimiter(start) || isLineDelimiter(start + length)) {
-        // the start offset or end offset of the selection range is inside a 
-        // multi byte line delimiter. This is an illegal operation and an exception 
+        // the start offset or end offset of the selection range is inside a
+        // multi byte line delimiter. This is an illegal operation and an exception
         // is thrown. Fixes 1GDKK3R
         DWT.error(DWT.ERROR_INVALID_ARGUMENT);
     }
     setSelection(start, length, false);
     setCaretLocation();
 }
-/** 
+/**
  * Adds the specified style.
  * <p>
  * The new style overwrites existing styles for the specified range.
- * Existing style ranges are adjusted if they partially overlap with 
- * the new style. To clear an individual style, call setStyleRange 
- * with a StyleRange that has null attributes. 
+ * Existing style ranges are adjusted if they partially overlap with
+ * the new style. To clear an individual style, call setStyleRange
+ * with a StyleRange that has null attributes.
  * </p><p>
- * Should not be called if a LineStyleListener has been set since the 
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p>
  *
@@ -7755,7 +7910,7 @@
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *   <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li> 
+ *   <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
  * </ul>
  */
 public void setStyleRange(StyleRange range) {
@@ -7765,14 +7920,14 @@
         if (range.isUnstyled()) {
             setStyleRanges(range.start, range.length, null, null, false);
         } else {
-            setStyleRanges(range.start, 0, null, new StyleRange[]{range}, false);
+            setStyleRanges(range.start, 0, null, [range], false);
         }
     } else {
         setStyleRanges(0, 0, null, null, true);
     }
 }
-/** 
- * Clears the styles in the range specified by <code>start</code> and 
+/**
+ * Clears the styles in the range specified by <code>start</code> and
  * <code>length</code> and adds the new styles.
  * <p>
  * The ranges array contains start and length pairs.  Each pair refers to
@@ -7784,7 +7939,7 @@
  * Note: It is expected that the same instance of a StyleRange will occur
  * multiple times within the styles array, reducing memory usage.
  * </p><p>
- * Should not be called if a LineStyleListener has been set since the 
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p>
  *
@@ -7792,19 +7947,19 @@
  * @param length length of the range to delete styles in
  * @param ranges the array of ranges.  The ranges must not overlap and must be in order.
  * @param styles the array of StyleRanges.  The range fields within the StyleRange are unused.
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
- *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 is styles.length)</li> 
- *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li> 
- *    <li>ERROR_INVALID_RANGE when a range overlaps</li> 
- * </ul>
- * 
- * @since 3.2 
+ *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 is styles.length)</li>
+ *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li>
+ *    <li>ERROR_INVALID_RANGE when a range overlaps</li>
+ * </ul>
+ *
+ * @since 3.2
  */
 public void setStyleRanges(int start, int length, int[] ranges, StyleRange[] styles) {
     checkWidget();
@@ -7815,7 +7970,7 @@
         setStyleRanges(start, length, ranges, styles, false);
     }
 }
-/** 
+/**
  * Sets styles to be used for rendering the widget content.
  * <p>
  * All styles in the widget will be replaced with the given set of ranges and styles.
@@ -7828,25 +7983,25 @@
  * Note: It is expected that the same instance of a StyleRange will occur
  * multiple times within the styles array, reducing memory usage.
  * </p><p>
- * Should not be called if a LineStyleListener has been set since the 
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p>
  *
  * @param ranges the array of ranges.  The ranges must not overlap and must be in order.
  * @param styles the array of StyleRanges.  The range fields within the StyleRange are unused.
- * 
+ *
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
  *    <li>ERROR_NULL_ARGUMENT when an element in the styles array is null</li>
- *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 is styles.length)</li> 
- *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li> 
- *    <li>ERROR_INVALID_RANGE when a range overlaps</li> 
- * </ul>
- * 
- * @since 3.2 
+ *    <li>ERROR_INVALID_RANGE when the number of ranges and style do not match (ranges.length * 2 is styles.length)</li>
+ *    <li>ERROR_INVALID_RANGE when a range is outside the valid range (> getCharCount() or less than zero)</li>
+ *    <li>ERROR_INVALID_RANGE when a range overlaps</li>
+ * </ul>
+ *
+ * @since 3.2
  */
 public void setStyleRanges(int[] ranges, StyleRange[] styles) {
     checkWidget();
@@ -7871,7 +8026,7 @@
             if (ranges.length !is styles.length << 1) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
         }
         int lastOffset = 0;
-        bool variableHeight = false; 
+        bool variableHeight = false;
         for (int i = 0; i < styles.length; i ++) {
             if (styles[i] is null) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
             int rangeStart, rangeLength;
@@ -7882,7 +8037,7 @@
                 rangeStart = styles[i].start;
                 rangeLength = styles[i].length;
             }
-            if (rangeLength < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); 
+            if (rangeLength < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
             if (!(0 <= rangeStart && rangeStart + rangeLength <= charCount)) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
             if (lastOffset > rangeStart) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
             variableHeight |= styles[i].isVariableHeight();
@@ -7941,13 +8096,13 @@
                 }
                 height = newLastLineBottom - y;
             }
-            super.redraw(0, y, clientAreaWidth, height, false);     
+            super.redraw(0, y, clientAreaWidth, height, false);
         }
     }
     setCaretLocation();
 }
-/** 
- * Sets styles to be used for rendering the widget content. All styles 
+/**
+ * Sets styles to be used for rendering the widget content. All styles
  * in the widget will be replaced with the given set of styles.
  * <p>
  * Note: Because a StyleRange includes the start and length, the
@@ -7956,32 +8111,32 @@
  * multiple StyleRanges, <code>setStyleRanges(int[], StyleRange[])</code>
  * can be used to share styles and reduce memory usage.
  * </p><p>
- * Should not be called if a LineStyleListener has been set since the 
+ * Should not be called if a LineStyleListener has been set since the
  * listener maintains the styles.
  * </p>
  *
  * @param ranges StyleRange objects containing the style information.
- * The ranges should not overlap. The style rendering is undefined if 
+ * The ranges should not overlap. The style rendering is undefined if
  * the ranges do overlap. Must not be null. The styles need to be in order.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
  * @exception IllegalArgumentException <ul>
- *    <li>ERROR_NULL_ARGUMENT when the list of ranges is null</li>
- *    <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li> 
- * </ul>
- * 
+ *    <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li>
+ * </ul>
+ *
  * @see #setStyleRanges(int[], StyleRange[])
  */
 public void setStyleRanges(StyleRange[] ranges) {
     checkWidget();
     if (isListening(LineGetStyle)) return;
-    if (ranges is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    // DWT extension: allow null for zero length string
+    //if (ranges is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
     setStyleRanges(0, 0, null, ranges, true);
 }
-/** 
- * Sets the tab width. 
+/**
+ * Sets the tab width.
  *
  * @param tabs tab width measured in characters.
  * @exception DWTException <ul>
@@ -7990,56 +8145,54 @@
  * </ul>
  */
 public void setTabs(int tabs) {
-    checkWidget();  
+    checkWidget();
     tabLength = tabs;
     renderer.setFont(null, tabs);
     resetCache(0, content.getLineCount());
     setCaretLocation();
     super.redraw();
 }
-/** 
- * Sets the widget content. 
- * If the widget has the DWT.SINGLE style and "text" contains more than 
- * one line, only the first line is rendered but the text is stored 
- * unchanged. A subsequent call to getText will return the same text 
+/**
+ * Sets the widget content.
+ * If the widget has the DWT.SINGLE style and "text" contains more than
+ * one line, only the first line is rendered but the text is stored
+ * unchanged. A subsequent call to getText will return the same text
  * that was set.
  * <p>
- * <b>Note:</b> Only a single line of text should be set when the DWT.SINGLE 
+ * <b>Note:</b> Only a single line of text should be set when the DWT.SINGLE
  * style is used.
  * </p>
  *
- * @param text new widget content. Replaces existing content. Line styles 
+ * @param text new widget content. Replaces existing content. Line styles
  *  that were set using StyledText API are discarded.  The
  *  current selection is also discarded.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
  * </ul>
- * @exception IllegalArgumentException <ul>
- *    <li>ERROR_NULL_ARGUMENT when String is null</li>
- * </ul>
  */
 public void setText(String text) {
     checkWidget();
-    if (text is null) {
-        DWT.error(DWT.ERROR_NULL_ARGUMENT);
-    }
+    // DWT extension: allow null for zero length string
+//     if (text is null) {
+//         DWT.error(DWT.ERROR_NULL_ARGUMENT);
+//     }
     Event event = new Event();
     event.start = 0;
     event.end = getCharCount();
     event.text = text;
-    event.doit = true;  
+    event.doit = true;
     notifyListeners(DWT.Verify, event);
     if (event.doit) {
         StyledTextEvent styledTextEvent = null;
         if (isListening(ExtendedModify)) {
             styledTextEvent = new StyledTextEvent(content);
             styledTextEvent.start = event.start;
-            styledTextEvent.end = event.start + event.text.length();
+            styledTextEvent.end = event.start + event.text.length;
             styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
         }
         content.setText(event.text);
-        sendModifyEvent(event); 
+        sendModifyEvent(event);
         if (styledTextEvent !is null) {
             notifyListeners(ExtendedModify, styledTextEvent);
         }
@@ -8071,15 +8224,15 @@
 /**
  * Sets the top index. Do nothing if there is no text set.
  * <p>
- * The top index is the index of the line that is currently at the top 
+ * The top index is the index of the line that is currently at the top
  * of the widget. The top index changes when the widget is scrolled.
  * Indexing starts from zero.
  * Note: The top index is reset to 0 when new text is set in the widget.
  * </p>
  *
- * @param topIndex new top index. Must be between 0 and 
- *  getLineCount() - fully visible lines per page. If no lines are fully 
- *  visible the maximum value is getLineCount() - 1. An out of range 
+ * @param topIndex new top index. Must be between 0 and
+ *  getLineCount() - fully visible lines per page. If no lines are fully
+ *  visible the maximum value is getLineCount() - 1. An out of range
  *  index will be adjusted accordingly.
  * @exception DWTException <ul>
  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -8108,7 +8261,7 @@
         } else {
             pixel = getAvailableHeightAbove(pixel);
         }
-    } 
+    }
     scrollVertical(pixel, true);
 }
 /**
@@ -8120,7 +8273,7 @@
  * Note: The top pixel is reset to 0 when new text is set in the widget.
  * </p>
  *
- * @param pixel new top pixel offset. Must be between 0 and 
+ * @param pixel new top pixel offset. Must be between 0 and
  *  (getLineCount() - visible lines per page) / getLineHeight()). An out
  *  of range offset will be adjusted accordingly.
  * @exception DWTException <ul>
@@ -8133,7 +8286,7 @@
     checkWidget();
     if (getCharCount() is 0) {
         return;
-    }   
+    }
     if (pixel < 0) pixel = 0;
     int lineCount = content.getLineCount();
     int height = clientAreaHeight - topMargin - bottomMargin;
@@ -8141,7 +8294,7 @@
     if (isFixedLineHeight()) {
         int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
         if (pixel > maxTopPixel) pixel = maxTopPixel;
-        pixel -= verticalOffset; 
+        pixel -= verticalOffset;
     } else {
         pixel -= verticalOffset;
         if (pixel > 0) {
@@ -8175,6 +8328,7 @@
     setCaretLocation();
     super.redraw();
 }
+// DWT: If necessary, scroll to show the location
 bool showLocation(Rectangle rect, bool scrollPage) {
     int clientAreaWidth = this.clientAreaWidth - leftMargin - rightMargin;
     int clientAreaHeight = this.clientAreaHeight - topMargin - bottomMargin;
@@ -8236,10 +8390,10 @@
         startOffset = selection.x;
         endOffset = selection.y;
     }
-    
+
     Rectangle startBounds = getBoundsAtOffset(startOffset);
     Rectangle endBounds = getBoundsAtOffset(endOffset);
-    
+
     // can the selection be fully displayed within the widget's visible width?
     int w = clientAreaWidth - leftMargin - rightMargin;
     bool selectionFits = rightToLeft ? startBounds.x - endBounds.x <= w : endBounds.x - startBounds.x <= w;
@@ -8254,7 +8408,7 @@
         endBounds.width = 0;
         showLocation(endBounds, false);
     } else {
-        // just show the end of the selection since the selection start 
+        // just show the end of the selection since the selection start
         // will not be visible
         showLocation(endBounds, true);
     }
@@ -8262,10 +8416,10 @@
 /**
  * Updates the selection and caret position depending on the text change.
  * <p>
- * If the selection intersects with the replaced text, the selection is 
+ * If the selection intersects with the replaced text, the selection is
  * reset and the caret moved to the end of the new text.
  * If the selection is behind the replaced text it is moved so that the
- * same text remains selected.  If the selection is before the replaced text 
+ * same text remains selected.  If the selection is before the replaced text
  * it is left unchanged.
  * </p>
  *
@@ -8276,6 +8430,7 @@
 void updateSelection(int startOffset, int replacedLength, int newLength) {
     if (selection.y <= startOffset) {
         // selection ends before text change
+        if (wordWrap) setCaretLocation();
         return;
     }
     if (selection.x < startOffset) {
@@ -8284,7 +8439,7 @@
     }
     if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
         // clear selection fragment after text change.
-        // do this only when the selection is actually affected by the 
+        // do this only when the selection is actually affected by the
         // change. Selection is only affected if it intersects the change (1GDY217).
         int netNewLength = newLength - replacedLength;
         int redrawStart = startOffset + newLength;