summaryrefslogtreecommitdiffstats
path: root/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse
diff options
context:
space:
mode:
authorVeronika Irvine <veronika>2003-03-05 15:00:14 +0000
committerVeronika Irvine <veronika>2003-03-05 15:00:14 +0000
commit281dc95420976f0ffb0cbab0dc7e5e4a8f3ac547 (patch)
tree7682936f0fef30a163a6b92d96ef435e09c564c1 /bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse
parent4d0b24a27232ff40c8169511178d0b60b9efdcc2 (diff)
downloadeclipse.platform.swt-281dc95420976f0ffb0cbab0dc7e5e4a8f3ac547.tar.gz
eclipse.platform.swt-281dc95420976f0ffb0cbab0dc7e5e4a8f3ac547.tar.xz
eclipse.platform.swt-281dc95420976f0ffb0cbab0dc7e5e4a8f3ac547.zip
Converting Binary files to ASCII
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse')
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/AnimatedProgress.java424
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentEvent.java132
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentListener.java58
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BusyIndicator.java152
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CCombo.java2352
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CLabel.java1216
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java4198
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderAdapter.java16
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderEvent.java32
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderListener.java16
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java1028
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ControlEditor.java476
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultContent.java1750
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java1354
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyEvent.java42
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyListener.java40
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundEvent.java48
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundListener.java38
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleEvent.java42
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleListener.java36
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/PopupList.java494
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ST.java118
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java892
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ScrolledComposite.java1160
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StackLayout.java222
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyleRange.java302
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java16526
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java2106
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextContent.java396
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextEvent.java62
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextListener.java140
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableCursor.java1022
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableEditor.java380
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTree.java1478
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeEditor.java454
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeItem.java1600
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangeListener.java108
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangedEvent.java44
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangingEvent.java118
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TreeEditor.java318
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/VerifyKeyListener.java32
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ViewForm.java1222
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/WrappedContent.java1326
43 files changed, 21985 insertions, 21985 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/AnimatedProgress.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/AnimatedProgress.java
index c21d07ab14..475f4e2e4d 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/AnimatedProgress.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/AnimatedProgress.java
@@ -1,215 +1,215 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.events.*;
-
-/**
- * A control for showing progress feedback for a long running operation.
- *
- * @deprecated As of Eclipse 2.1, use ProgressBar with the style SWT.INDETERMINATE
- *
- * <dl>
- * <dt><b>Styles:</b><dd>VERTICAL, HORIZONTAL, BORDER
- * </dl>
- */
-public class AnimatedProgress extends Canvas {
-
- private static final int SLEEP = 70;
- private static final int DEFAULT_WIDTH = 160;
- private static final int DEFAULT_HEIGHT = 18;
- private boolean active = false;
- private boolean showStripes = false;
- private int value;
- private int orientation = SWT.HORIZONTAL;
- private boolean showBorder = false;
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#VERTICAL
- * @see SWT#HORIZONTAL
- * @see SWT#BORDER
- * @see #getStyle
- */
-public AnimatedProgress(Composite parent, int style) {
- super(parent, checkStyle(style));
-
- if ((style & SWT.VERTICAL) != 0) {
- orientation = SWT.VERTICAL;
- }
- showBorder = (style & SWT.BORDER) != 0;
-
- addControlListener(new ControlAdapter() {
- public void controlResized(ControlEvent e) {
- redraw();
- }
- });
- addPaintListener(new PaintListener() {
- public void paintControl(PaintEvent e) {
- paint(e);
- }
- });
- addDisposeListener(new DisposeListener() {
- public void widgetDisposed(DisposeEvent e){
- stop();
- }
- });
-}
-private static int checkStyle (int style) {
- int mask = SWT.NONE;
- return style & mask;
-}
-/**
- * Stop the animation if it is not already stopped and
- * reset the presentation to a blank appearance.
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public synchronized void clear(){
- checkWidget();
- if (active) stop();
- showStripes = false;
- redraw();
-}
-public Point computeSize(int wHint, int hHint, boolean changed) {
- checkWidget();
- Point size = null;
- if (orientation == SWT.HORIZONTAL) {
- size = new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT);
- } else {
- size = new Point(DEFAULT_HEIGHT, DEFAULT_WIDTH);
- }
- if (wHint != SWT.DEFAULT) size.x = wHint;
- if (hHint != SWT.DEFAULT) size.y = hHint;
-
- return size;
-}
-private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
- gc.setForeground(topleft);
- gc.drawLine(x, y, x+w-1, y);
- gc.drawLine(x, y, x, y+h-1);
-
- gc.setForeground(bottomright);
- gc.drawLine(x+w, y, x+w, y+h);
- gc.drawLine(x, y+h, x+w, y+h);
-}
-private void paint(PaintEvent event) {
- GC gc = event.gc;
- Display disp= getDisplay();
-
- Rectangle rect= getClientArea();
- gc.fillRectangle(rect);
- if (showBorder) {
- drawBevelRect(gc, rect.x, rect.y, rect.width-1, rect.height-1,
- disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW),
- disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
- }
-
- paintStripes(gc);
-}
-private void paintStripes(GC gc) {
-
- if (!showStripes) return;
-
- Rectangle rect= getClientArea();
- // Subtracted border painted by paint.
- rect = new Rectangle(rect.x+2, rect.y+2, rect.width-4, rect.height-4);
-
- gc.setLineWidth(2);
- gc.setClipping(rect);
- Color color = getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
- gc.setBackground(color);
- gc.fillRectangle(rect);
- gc.setForeground(this.getBackground());
- int step = 12;
- int foregroundValue = value == 0 ? step - 2 : value - 2;
- if (orientation == SWT.HORIZONTAL) {
- int y = rect.y - 1;
- int w = rect.width;
- int h = rect.height + 2;
- for (int i= 0; i < w; i+= step) {
- int x = i + foregroundValue;
- gc.drawLine(x, y, x, h);
- }
- } else {
- int x = rect.x - 1;
- int w = rect.width + 2;
- int h = rect.height;
-
- for (int i= 0; i < h; i+= step) {
- int y = i + foregroundValue;
- gc.drawLine(x, y, w, y);
- }
- }
-
- if (active) {
- value = (value + 2) % step;
- }
-}
-/**
-* Start the animation.
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* </ul>
-*/
-public synchronized void start() {
- checkWidget();
- if (active) return;
-
- active = true;
- showStripes = true;
-
- final Display display = getDisplay();
- final Runnable [] timer = new Runnable [1];
- timer [0] = new Runnable () {
- public void run () {
- if (!active) return;
- GC gc = new GC(AnimatedProgress.this);
- paintStripes(gc);
- gc.dispose();
- display.timerExec (SLEEP, timer [0]);
- }
- };
- display.timerExec (SLEEP, timer [0]);
-}
-/**
-* Stop the animation. Freeze the presentation at its current appearance.
-*/
-public synchronized void stop() {
- //checkWidget();
- active = false;
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * A control for showing progress feedback for a long running operation.
+ *
+ * @deprecated As of Eclipse 2.1, use ProgressBar with the style SWT.INDETERMINATE
+ *
+ * <dl>
+ * <dt><b>Styles:</b><dd>VERTICAL, HORIZONTAL, BORDER
+ * </dl>
+ */
+public class AnimatedProgress extends Canvas {
+
+ private static final int SLEEP = 70;
+ private static final int DEFAULT_WIDTH = 160;
+ private static final int DEFAULT_HEIGHT = 18;
+ private boolean active = false;
+ private boolean showStripes = false;
+ private int value;
+ private int orientation = SWT.HORIZONTAL;
+ private boolean showBorder = false;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#VERTICAL
+ * @see SWT#HORIZONTAL
+ * @see SWT#BORDER
+ * @see #getStyle
+ */
+public AnimatedProgress(Composite parent, int style) {
+ super(parent, checkStyle(style));
+
+ if ((style & SWT.VERTICAL) != 0) {
+ orientation = SWT.VERTICAL;
+ }
+ showBorder = (style & SWT.BORDER) != 0;
+
+ addControlListener(new ControlAdapter() {
+ public void controlResized(ControlEvent e) {
+ redraw();
+ }
+ });
+ addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ paint(e);
+ }
+ });
+ addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e){
+ stop();
+ }
+ });
+}
+private static int checkStyle (int style) {
+ int mask = SWT.NONE;
+ return style & mask;
+}
+/**
+ * Stop the animation if it is not already stopped and
+ * reset the presentation to a blank appearance.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public synchronized void clear(){
+ checkWidget();
+ if (active) stop();
+ showStripes = false;
+ redraw();
+}
+public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ Point size = null;
+ if (orientation == SWT.HORIZONTAL) {
+ size = new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ } else {
+ size = new Point(DEFAULT_HEIGHT, DEFAULT_WIDTH);
+ }
+ if (wHint != SWT.DEFAULT) size.x = wHint;
+ if (hHint != SWT.DEFAULT) size.y = hHint;
+
+ return size;
+}
+private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
+ gc.setForeground(topleft);
+ gc.drawLine(x, y, x+w-1, y);
+ gc.drawLine(x, y, x, y+h-1);
+
+ gc.setForeground(bottomright);
+ gc.drawLine(x+w, y, x+w, y+h);
+ gc.drawLine(x, y+h, x+w, y+h);
+}
+private void paint(PaintEvent event) {
+ GC gc = event.gc;
+ Display disp= getDisplay();
+
+ Rectangle rect= getClientArea();
+ gc.fillRectangle(rect);
+ if (showBorder) {
+ drawBevelRect(gc, rect.x, rect.y, rect.width-1, rect.height-1,
+ disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW),
+ disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+ }
+
+ paintStripes(gc);
+}
+private void paintStripes(GC gc) {
+
+ if (!showStripes) return;
+
+ Rectangle rect= getClientArea();
+ // Subtracted border painted by paint.
+ rect = new Rectangle(rect.x+2, rect.y+2, rect.width-4, rect.height-4);
+
+ gc.setLineWidth(2);
+ gc.setClipping(rect);
+ Color color = getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
+ gc.setBackground(color);
+ gc.fillRectangle(rect);
+ gc.setForeground(this.getBackground());
+ int step = 12;
+ int foregroundValue = value == 0 ? step - 2 : value - 2;
+ if (orientation == SWT.HORIZONTAL) {
+ int y = rect.y - 1;
+ int w = rect.width;
+ int h = rect.height + 2;
+ for (int i= 0; i < w; i+= step) {
+ int x = i + foregroundValue;
+ gc.drawLine(x, y, x, h);
+ }
+ } else {
+ int x = rect.x - 1;
+ int w = rect.width + 2;
+ int h = rect.height;
+
+ for (int i= 0; i < h; i+= step) {
+ int y = i + foregroundValue;
+ gc.drawLine(x, y, w, y);
+ }
+ }
+
+ if (active) {
+ value = (value + 2) % step;
+ }
+}
+/**
+* Start the animation.
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* </ul>
+*/
+public synchronized void start() {
+ checkWidget();
+ if (active) return;
+
+ active = true;
+ showStripes = true;
+
+ final Display display = getDisplay();
+ final Runnable [] timer = new Runnable [1];
+ timer [0] = new Runnable () {
+ public void run () {
+ if (!active) return;
+ GC gc = new GC(AnimatedProgress.this);
+ paintStripes(gc);
+ gc.dispose();
+ display.timerExec (SLEEP, timer [0]);
+ }
+ };
+ display.timerExec (SLEEP, timer [0]);
+}
+/**
+* Stop the animation. Freeze the presentation at its current appearance.
+*/
+public synchronized void stop() {
+ //checkWidget();
+ active = false;
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentEvent.java
index 45125d77b8..dd13c1aa72 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentEvent.java
@@ -1,69 +1,69 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-
-/**
- * This event is sent to BidiSegmentListeners when a line is to
- * be measured or rendered in a bidi locale. The segments field is
- * used to specify text ranges in the line that should be treated as
- * separate segments for bidi reordering. Each segment will be reordered
- * and rendered separately.
- * <p>
- * The elements in the segments field specify the start offset of
- * a segment relative to the start of the line. They must follow
- * the following rules:
- * <ul>
- * <li>first element must be 0
- * <li>elements must be in ascending order and must not have duplicates
- * <li>elements must not exceed the line length
- * </ul>
- * In addition, the last element may be set to the end of the line
- * but this is not required.
- *
- * The segments field may be left null if the entire line should
- * be reordered as is.
- * </p>
- * A BidiSegmentListener may be used when adjacent segments of
- * right-to-left text should not be reordered relative to each other.
- * For example, within a Java editor, you may wish multiple
- * right-to-left string literals to be reordered differently than the
- * bidi algorithm specifies.
- *
- * Example:
- * <pre>
- * stored line = "R1R2R3" + "R4R5R6"
- * R1 to R6 are right-to-left characters. The quotation marks
- * are part of the line text. The line is 13 characters long.
- *
- * segments = null:
- * entire line will be reordered and thus the two R2L segments
- * swapped (as per the bidi algorithm).
- * visual line (rendered on screen) = "R6R5R4" + "R3R2R1"
- *
- * segments = [0, 5, 8]
- * "R1R2R3" will be reordered, followed by [blank]+[blank] and
- * "R4R5R6".
- * visual line = "R3R2R1" + "R6R5R4"
- * </pre>
- */
-public class BidiSegmentEvent extends TypedEvent {
- /** line start offset */
- public int lineOffset;
- /** line text */
- public String lineText;
- /** bidi segments, see above */
- public int[] segments;
-
-BidiSegmentEvent(StyledTextEvent e) {
- super(e);
- lineOffset = e.detail;
- lineText = e.text;
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+
+/**
+ * This event is sent to BidiSegmentListeners when a line is to
+ * be measured or rendered in a bidi locale. The segments field is
+ * used to specify text ranges in the line that should be treated as
+ * separate segments for bidi reordering. Each segment will be reordered
+ * and rendered separately.
+ * <p>
+ * The elements in the segments field specify the start offset of
+ * a segment relative to the start of the line. They must follow
+ * the following rules:
+ * <ul>
+ * <li>first element must be 0
+ * <li>elements must be in ascending order and must not have duplicates
+ * <li>elements must not exceed the line length
+ * </ul>
+ * In addition, the last element may be set to the end of the line
+ * but this is not required.
+ *
+ * The segments field may be left null if the entire line should
+ * be reordered as is.
+ * </p>
+ * A BidiSegmentListener may be used when adjacent segments of
+ * right-to-left text should not be reordered relative to each other.
+ * For example, within a Java editor, you may wish multiple
+ * right-to-left string literals to be reordered differently than the
+ * bidi algorithm specifies.
+ *
+ * Example:
+ * <pre>
+ * stored line = "R1R2R3" + "R4R5R6"
+ * R1 to R6 are right-to-left characters. The quotation marks
+ * are part of the line text. The line is 13 characters long.
+ *
+ * segments = null:
+ * entire line will be reordered and thus the two R2L segments
+ * swapped (as per the bidi algorithm).
+ * visual line (rendered on screen) = "R6R5R4" + "R3R2R1"
+ *
+ * segments = [0, 5, 8]
+ * "R1R2R3" will be reordered, followed by [blank]+[blank] and
+ * "R4R5R6".
+ * visual line = "R3R2R1" + "R6R5R4"
+ * </pre>
+ */
+public class BidiSegmentEvent extends TypedEvent {
+ /** line start offset */
+ public int lineOffset;
+ /** line text */
+ public String lineText;
+ /** bidi segments, see above */
+ public int[] segments;
+
+BidiSegmentEvent(StyledTextEvent e) {
+ super(e);
+ lineOffset = e.detail;
+ lineText = e.text;
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentListener.java
index 551551afe3..267f5dbe2b 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BidiSegmentListener.java
@@ -1,32 +1,32 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-import org.eclipse.swt.internal.SWTEventListener;
-
-/**
- * This listener interface may be implemented in order to receive
- * BidiSegmentEvents.
- * @see BidiSegmentEvent
- */
-public interface BidiSegmentListener extends SWTEventListener {
-
-/**
- * This method is called when a line needs to be reordered for
- * measuring or rendering in a bidi locale.
- * <p>
- *
- * @param event.lineOffset line start offset (input)
- * @param event.lineText line text (input)
- * @param event.segments text segments that should be reordered
- * separately. (output)
- * @see BidiSegmentEvent
- */
-public void lineGetSegments(BidiSegmentEvent event);
-
-}
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+import org.eclipse.swt.internal.SWTEventListener;
+
+/**
+ * This listener interface may be implemented in order to receive
+ * BidiSegmentEvents.
+ * @see BidiSegmentEvent
+ */
+public interface BidiSegmentListener extends SWTEventListener {
+
+/**
+ * This method is called when a line needs to be reordered for
+ * measuring or rendering in a bidi locale.
+ * <p>
+ *
+ * @param event.lineOffset line start offset (input)
+ * @param event.lineText line text (input)
+ * @param event.segments text segments that should be reordered
+ * separately. (output)
+ * @see BidiSegmentEvent
+ */
+public void lineGetSegments(BidiSegmentEvent event);
+
+}
+
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BusyIndicator.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BusyIndicator.java
index b9268db080..5e4df66c07 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BusyIndicator.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/BusyIndicator.java
@@ -1,80 +1,80 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * Support for showing a Busy Cursor during a long running process.
- */
-public class BusyIndicator {
-
- static int nextBusyId = 1;
- static final String BUSYID_NAME = "SWT BusyIndicator"; //$NON-NLS-1$
-
-/**
- * Runs the given <code>Runnable</code> while providing
- * busy feedback using this busy indicator.
- *
- * @param display the display on which the busy feedback should be
- * displayed. If the display is null, the Display for the current
- * thread will be used. If there is no Display for the current thread,
- * the runnable code will be executed and no busy feedback will be displayed.
- * @param runnable the runnable for which busy feedback is to be shown.
- * Must not be null.
- *
-* @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the runnable is null</li>
- * </ul>
- *
- * @see #showWhile
- */
-
-public static void showWhile(Display display, Runnable runnable) {
- if (runnable == null)
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- if (display == null) {
- display = Display.getCurrent();
- if (display == null) {
- runnable.run();
- return;
- }
- }
-
- Integer busyId = new Integer(nextBusyId);
- nextBusyId++;
- Cursor cursor = new Cursor(display, SWT.CURSOR_WAIT);
-
- Shell[] shells = display.getShells();
- for (int i = 0; i < shells.length; i++) {
- Integer id = (Integer)shells[i].getData(BUSYID_NAME);
- if (id == null) {
- shells[i].setCursor(cursor);
- shells[i].setData(BUSYID_NAME, busyId);
- }
- }
-
- try {
- runnable.run();
- } finally {
- shells = display.getShells();
- for (int i = 0; i < shells.length; i++) {
- Integer id = (Integer)shells[i].getData(BUSYID_NAME);
- if (id == busyId) {
- shells[i].setCursor(null);
- shells[i].setData(BUSYID_NAME, null);
- }
- }
- if (cursor != null && !cursor.isDisposed()) {
- cursor.dispose();
- }
- }
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * Support for showing a Busy Cursor during a long running process.
+ */
+public class BusyIndicator {
+
+ static int nextBusyId = 1;
+ static final String BUSYID_NAME = "SWT BusyIndicator"; //$NON-NLS-1$
+
+/**
+ * Runs the given <code>Runnable</code> while providing
+ * busy feedback using this busy indicator.
+ *
+ * @param display the display on which the busy feedback should be
+ * displayed. If the display is null, the Display for the current
+ * thread will be used. If there is no Display for the current thread,
+ * the runnable code will be executed and no busy feedback will be displayed.
+ * @param runnable the runnable for which busy feedback is to be shown.
+ * Must not be null.
+ *
+* @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the runnable is null</li>
+ * </ul>
+ *
+ * @see #showWhile
+ */
+
+public static void showWhile(Display display, Runnable runnable) {
+ if (runnable == null)
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ if (display == null) {
+ display = Display.getCurrent();
+ if (display == null) {
+ runnable.run();
+ return;
+ }
+ }
+
+ Integer busyId = new Integer(nextBusyId);
+ nextBusyId++;
+ Cursor cursor = new Cursor(display, SWT.CURSOR_WAIT);
+
+ Shell[] shells = display.getShells();
+ for (int i = 0; i < shells.length; i++) {
+ Integer id = (Integer)shells[i].getData(BUSYID_NAME);
+ if (id == null) {
+ shells[i].setCursor(cursor);
+ shells[i].setData(BUSYID_NAME, busyId);
+ }
+ }
+
+ try {
+ runnable.run();
+ } finally {
+ shells = display.getShells();
+ for (int i = 0; i < shells.length; i++) {
+ Integer id = (Integer)shells[i].getData(BUSYID_NAME);
+ if (id == busyId) {
+ shells[i].setCursor(null);
+ shells[i].setData(BUSYID_NAME, null);
+ }
+ }
+ if (cursor != null && !cursor.isDisposed()) {
+ cursor.dispose();
+ }
+ }
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CCombo.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CCombo.java
index e484e9fc94..76031ae785 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CCombo.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CCombo.java
@@ -1,1180 +1,1180 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.accessibility.*;
-
-/**
- * The CCombo class represents a selectable user interface object
- * that combines a text field and a list and issues notificiation
- * when an item is selected from the list.
- * <p>
- * Note that although this class is a subclass of <code>Composite</code>,
- * it does not make sense to add children to it, or set a layout on it.
- * </p>
- * <dl>
- * <dt><b>Styles:</b>
- * <dd>BORDER, READ_ONLY, FLAT</dd>
- * <dt><b>Events:</b>
- * <dd>Selection</dd>
- * </dl>
- */
-public final class CCombo extends Composite {
-
- static final int ITEMS_SHOWING = 5;
-
- Text text;
- List list;
- Shell popup;
- Button arrow;
- boolean hasFocus;
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#BORDER
- * @see SWT#READ_ONLY
- * @see SWT#FLAT
- * @see Widget#getStyle
- */
-public CCombo (Composite parent, int style) {
- super (parent, checkStyle (style));
-
- style = getStyle();
-
- int textStyle = SWT.SINGLE;
- if ((style & SWT.READ_ONLY) != 0) textStyle |= SWT.READ_ONLY;
- if ((style & SWT.FLAT) != 0) textStyle |= SWT.FLAT;
- text = new Text (this, textStyle);
-
- popup = new Shell (getShell (), SWT.NO_TRIM);
-
- int listStyle = SWT.SINGLE | SWT.V_SCROLL;
- if ((style & SWT.FLAT) != 0) listStyle |= SWT.FLAT;
- list = new List (popup, listStyle);
-
- int arrowStyle = SWT.ARROW | SWT.DOWN;
- if ((style & SWT.FLAT) != 0) arrowStyle |= SWT.FLAT;
- arrow = new Button (this, arrowStyle);
-
- Listener listener = new Listener () {
- public void handleEvent (Event event) {
- if (popup == event.widget) {
- popupEvent (event);
- return;
- }
- if (text == event.widget) {
- textEvent (event);
- return;
- }
- if (list == event.widget) {
- listEvent (event);
- return;
- }
- if (arrow == event.widget) {
- arrowEvent (event);
- return;
- }
- if (CCombo.this == event.widget) {
- comboEvent (event);
- return;
- }
-
- }
- };
-
- int [] comboEvents = {SWT.Dispose, SWT.Move, SWT.Resize};
- for (int i=0; i<comboEvents.length; i++) this.addListener (comboEvents [i], listener);
-
- int [] popupEvents = {SWT.Close, SWT.Paint, SWT.Deactivate};
- for (int i=0; i<popupEvents.length; i++) popup.addListener (popupEvents [i], listener);
-
- int [] textEvents = {SWT.KeyDown, SWT.KeyUp, SWT.Modify, SWT.MouseDown, SWT.MouseUp, SWT.Traverse, SWT.FocusIn, SWT.FocusOut};
- for (int i=0; i<textEvents.length; i++) text.addListener (textEvents [i], listener);
-
- int [] listEvents = {SWT.MouseUp, SWT.Selection, SWT.Traverse, SWT.KeyDown, SWT.KeyUp, SWT.FocusIn, SWT.FocusOut};
- for (int i=0; i<listEvents.length; i++) list.addListener (listEvents [i], listener);
-
- int [] arrowEvents = {SWT.Selection, SWT.FocusIn, SWT.FocusOut};
- for (int i=0; i<arrowEvents.length; i++) arrow.addListener (arrowEvents [i], listener);
-
- initAccessible();
-}
-static int checkStyle (int style) {
- int mask = SWT.BORDER | SWT.READ_ONLY | SWT.FLAT;
- return style & mask;
-}
-/**
-* Adds an item.
-* <p>
-* The item is placed at the end of the list.
-* Indexing is zero based.
-*
-* @param string the new item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when the string is null
-* @exception SWTError(ERROR_ITEM_NOT_ADDED)
-* when the item cannot be added
-*/
-public void add (String string) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- list.add (string);
-}
-/**
-* Adds an item at an index.
-* <p>
-* The item is placed at an index in the list.
-* Indexing is zero based.
-*
-* This operation will fail when the index is
-* out of range.
-*
-* @param string the new item
-* @param index the index for the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when the string is null
-* @exception SWTError(ERROR_ITEM_NOT_ADDED)
-* when the item cannot be added
-*/
-public void add (String string, int index) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- list.add (string, index);
-}
-/**
-* Adds the listener to receive events.
-* <p>
-*
-* @param listener the listener
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when listener is null
-*/
-public void addModifyListener (ModifyListener listener) {;
- checkWidget();
- if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- TypedListener typedListener = new TypedListener (listener);
- addListener (SWT.Modify, typedListener);
-}
-/**
-* Adds the listener to receive events.
-* <p>
-*
-* @param listener the listener
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when listener is null
-*/
-public void addSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- TypedListener typedListener = new TypedListener (listener);
- addListener (SWT.Selection,typedListener);
- addListener (SWT.DefaultSelection,typedListener);
-}
-void arrowEvent (Event event) {
- switch (event.type) {
- case SWT.FocusIn: {
- if (hasFocus) return;
- hasFocus = true;
- if (getEditable ()) text.selectAll ();
- Event e = new Event();
- e.time = event.time;
- notifyListeners(SWT.FocusIn, e);
- break;
- }
- case SWT.FocusOut: {
- event.display.asyncExec(new Runnable() {
- public void run() {
- if (CCombo.this.isDisposed()) return;
- Control focusControl = getDisplay().getFocusControl();
- if (focusControl == list || focusControl == text) return;
- hasFocus = false;
- Event e = new Event();
- notifyListeners(SWT.FocusOut, e);
- }
- });
- break;
- }
- case SWT.Selection: {
- dropDown (!isDropped ());
- break;
- }
- }
-}
-/**
-* Clears the current selection.
-* <p>
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public void clearSelection () {
- checkWidget();
- text.clearSelection ();
- list.deselectAll ();
-}
-void comboEvent (Event event) {
- switch (event.type) {
- case SWT.Dispose:
- if (popup != null && !popup.isDisposed ()) popup.dispose ();
- popup = null;
- text = null;
- list = null;
- arrow = null;
- break;
- case SWT.Move:
- dropDown(false);
- break;
- case SWT.Resize:
- internalLayout();
- break;
- }
-}
-
-public Point computeSize (int wHint, int hHint, boolean changed) {
- checkWidget();
- int width = 0, height = 0;
- Point textSize = text.computeSize (wHint, SWT.DEFAULT, changed);
- Point arrowSize = arrow.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
- Point listSize = list.computeSize (wHint, SWT.DEFAULT, changed);
- int borderWidth = getBorderWidth();
-
- height = Math.max (hHint, Math.max(textSize.y, arrowSize.y) + 2*borderWidth);
- width = Math.max (wHint, Math.max(textSize.x + arrowSize.x + 2*borderWidth, listSize.x + 2) );
- return new Point (width, height);
-}
-/**
-* Deselects an item.
-* <p>
-* If the item at an index is selected, it is
-* deselected. If the item at an index is not
-* selected, it remains deselected. Indices
-* that are out of range are ignored. Indexing
-* is zero based.
-*
-* @param index the index of the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public void deselect (int index) {
- checkWidget();
- list.deselect (index);
-}
-/**
-* Deselects all items.
-* <p>
-*
-* If an item is selected, it is deselected.
-* If an item is not selected, it remains unselected.
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public void deselectAll () {
- checkWidget();
- list.deselectAll ();
-}
-void dropDown (boolean drop) {
- if (drop == isDropped ()) return;
- if (!drop) {
- popup.setVisible (false);
- text.setFocus();
- return;
- }
- int index = list.getSelectionIndex ();
- if (index != -1) list.setTopIndex (index);
- Rectangle listRect = list.getBounds ();
- Point point = getParent().toDisplay (getLocation ());
- Point comboSize = getSize();
- int width = Math.max (comboSize.x, listRect.width + 2);
- popup.setBounds (point.x, point.y + comboSize.y, width, listRect.height + 2);
- popup.setVisible (true);
- list.setFocus();
-}
-public Control [] getChildren () {
- checkWidget();
- return new Control [0];
-}
-boolean getEditable () {
- return text.getEditable ();
-}
-/**
-* Gets an item at an index.
-* <p>
-* Indexing is zero based.
-*
-* This operation will fail when the index is out
-* of range or an item could not be queried from
-* the OS.
-*
-* @param index the index of the item
-* @return the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_CANNOT_GET_ITEM)
-* when the operation fails
-*/
-public String getItem (int index) {
- checkWidget();
- return list.getItem (index);
-}
-/**
-* Gets the number of items.
-* <p>
-* This operation will fail if the number of
-* items could not be queried from the OS.
-*
-* @return the number of items in the widget
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_CANNOT_GET_COUNT)
-* when the operation fails
-*/
-public int getItemCount () {
- checkWidget();
- return list.getItemCount ();
-}
-/**
-* Gets the height of one item.
-* <p>
-* This operation will fail if the height of
-* one item could not be queried from the OS.
-*
-* @return the height of one item in the widget
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_CANNOT_GET_ITEM_HEIGHT)
-* when the operation fails
-*/
-public int getItemHeight () {
- checkWidget();
- return list.getItemHeight ();
-}
-/**
-* Gets the items.
-* <p>
-* This operation will fail if the items cannot
-* be queried from the OS.
-*
-* @return the items in the widget
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_CANNOT_GET_ITEM)
-* when the operation fails
-*/
-public String [] getItems () {
- checkWidget();
- return list.getItems ();
-}
-/**
-* Gets the selection.
-* <p>
-* @return a point representing the selection start and end
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public Point getSelection () {
- checkWidget();
- return text.getSelection ();
-}
-/**
-* Gets the index of the selected item.
-* <p>
-* Indexing is zero based.
-* If no item is selected -1 is returned.
-*
-* @return the index of the selected item.
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public int getSelectionIndex () {
- checkWidget();
- return list.getSelectionIndex ();
-}
-/**
-* Gets the widget text.
-* <p>
-* If the widget has no text, an empty string is returned.
-*
-* @return the widget text
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public String getText () {
- checkWidget();
- return text.getText ();
-}
-/**
-* Gets the height of the combo's text field.
-* <p>
-* The operation will fail if the height cannot
-* be queried from the OS.
-
-* @return the height of the combo's text field.
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_ERROR_CANNOT_GET_ITEM_HEIGHT)
-* when the operation fails
-*/
-public int getTextHeight () {
- checkWidget();
- return text.getLineHeight();
-}
-/**
-* Gets the text limit.
-* <p>
-* @return the text limit
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public int getTextLimit () {
- checkWidget();
- return text.getTextLimit ();
-}
-/**
-* Gets the index of an item.
-* <p>
-* The list is searched starting at 0 until an
-* item is found that is equal to the search item.
-* If no item is found, -1 is returned. Indexing
-* is zero based.
-*
-* @param string the search item
-* @return the index of the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when string is null
-*/
-public int indexOf (String string) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- return list.indexOf (string);
-}
-/**
-* Gets the index of an item.
-* <p>
-* The widget is searched starting at start including
-* the end position until an item is found that
-* is equal to the search itenm. If no item is
-* found, -1 is returned. Indexing is zero based.
-*
-* @param string the search item
-* @param index the starting position
-* @return the index of the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when string is null
-*/
-public int indexOf (String string, int start) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- return list.indexOf (string, start);
-}
-
-void initAccessible() {
- getAccessible().addAccessibleListener(new AccessibleAdapter() {
- public void getHelp(AccessibleEvent e) {
- e.result = getToolTipText();
- }
- });
-
- getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() {
- public void getChildAtPoint(AccessibleControlEvent e) {
- Point testPoint = toControl(new Point(e.x, e.y));
- if (getBounds().contains(testPoint)) {
- e.childID = ACC.CHILDID_SELF;
- }
- }
-
- public void getLocation(AccessibleControlEvent e) {
- Rectangle location = getBounds();
- Point pt = toDisplay(new Point(location.x, location.y));
- e.x = pt.x;
- e.y = pt.y;
- e.width = location.width;
- e.height = location.height;
- }
-
- public void getChildCount(AccessibleControlEvent e) {
- e.detail = 0;
- }
-
- public void getRole(AccessibleControlEvent e) {
- e.detail = ACC.ROLE_COMBOBOX;
- }
-
- public void getState(AccessibleControlEvent e) {
- e.detail = ACC.STATE_NORMAL;
- }
-
- public void getValue(AccessibleControlEvent e) {
- e.result = getText();
- }
- });
-}
-boolean isDropped () {
- return popup.getVisible ();
-}
-public boolean isFocusControl () {
- checkWidget();
- if (text.isFocusControl() || arrow.isFocusControl() || list.isFocusControl() || popup.isFocusControl()) {
- return true;
- } else {
- return super.isFocusControl();
- }
-}
-void internalLayout () {
- if (isDropped ()) dropDown (false);
-
- Rectangle rect = getClientArea();
- int width = rect.width;
- int height = rect.height;
- Point arrowSize = arrow.computeSize(SWT.DEFAULT, height);
- text.setBounds (0, 0, width - arrowSize.x, height);
- arrow.setBounds (width - arrowSize.x, 0, arrowSize.x, arrowSize.y);
-
- Point size = getSize();
- int itemHeight = list.getItemHeight () * ITEMS_SHOWING;
- Point listSize = list.computeSize (SWT.DEFAULT, itemHeight);
- list.setBounds (1, 1, Math.max (size.x - 2, listSize.x), listSize.y);
-}
-void listEvent (Event event) {
- switch (event.type) {
- case SWT.FocusIn: {
- if (hasFocus) return;
- hasFocus = true;
- if (getEditable ()) text.selectAll ();
- Event e = new Event();
- e.time = event.time;
- notifyListeners(SWT.FocusIn, e);
- break;
- }
- case SWT.FocusOut: {
- event.display.asyncExec(new Runnable() {
- public void run() {
- if (CCombo.this.isDisposed()) return;
- Control focusControl = getDisplay().getFocusControl();
- if (focusControl == text || focusControl == arrow) return;
- hasFocus = false;
- Event e = new Event();
- notifyListeners(SWT.FocusOut, e);
- }
- });
- break;
- }
- case SWT.MouseUp: {
- if (event.button != 1) return;
- dropDown (false);
- Event e = new Event();
- e.time = event.time;
- notifyListeners(SWT.DefaultSelection, e);
- break;
- }
- case SWT.Selection: {
- int index = list.getSelectionIndex ();
- if (index == -1) return;
- text.setText (list.getItem (index));
- text.selectAll ();
- list.setSelection(index);
- Event e = new Event();
- e.time = event.time;
- e.stateMask = event.stateMask;
- e.doit = event.doit;
- notifyListeners(SWT.Selection, e);
- event.doit = e.doit;
- break;
- }
- case SWT.Traverse: {
- switch (event.detail) {
- case SWT.TRAVERSE_TAB_NEXT:
- case SWT.TRAVERSE_RETURN:
- case SWT.TRAVERSE_ESCAPE:
- case SWT.TRAVERSE_ARROW_PREVIOUS:
- case SWT.TRAVERSE_ARROW_NEXT:
- event.doit = false;
- break;
- }
- Event e = new Event();
- e.time = event.time;
- e.detail = event.detail;
- e.doit = event.doit;
- e.keyCode = event.keyCode;
- notifyListeners(SWT.Traverse, e);
- event.doit = e.doit;
- break;
- }
- case SWT.KeyUp: {
- Event e = new Event();
- e.time = event.time;
- e.character = event.character;
- e.keyCode = event.keyCode;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.KeyUp, e);
- break;
- }
- case SWT.KeyDown: {
- if (event.character == SWT.ESC) {
- // escape key cancels popup list
- dropDown (false);
- }
- if (event.character == SWT.CR || event.character == '\t') {
- // Enter and Tab cause default selection
- dropDown (false);
- Event e = new Event();
- e.time = event.time;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.DefaultSelection, e);
- }
- //At this point the widget may have been disposed.
- // If so, do not continue.
- if (isDisposed()) break;
- Event e = new Event();
- e.time = event.time;
- e.character = event.character;
- e.keyCode = event.keyCode;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.KeyDown, e);
- break;
-
- }
- }
-}
-void popupEvent(Event event) {
- switch (event.type) {
- case SWT.Paint:
- // draw black rectangle around list
- Rectangle listRect = list.getBounds();
- Color black = getDisplay().getSystemColor(SWT.COLOR_BLACK);
- event.gc.setForeground(black);
- event.gc.drawRectangle(0, 0, listRect.width + 1, listRect.height + 1);
- break;
- case SWT.Close:
- event.doit = false;
- dropDown (false);
- break;
- case SWT.Deactivate:
- dropDown (false);
- break;
- }
-}
-public void redraw (int x, int y, int width, int height, boolean all) {
- checkWidget();
- if (!all) return;
- Point location = text.getLocation();
- text.redraw(x - location.x, y - location.y, width, height, all);
- location = list.getLocation();
- list.redraw(x - location.x, y - location.y, width, height, all);
- if (arrow != null) {
- location = arrow.getLocation();
- arrow.redraw(x - location.x, y - location.y, width, height, all);
- }
-}
-
-/**
-* Removes an item at an index.
-* <p>
-* Indexing is zero based.
-*
-* This operation will fail when the index is out
-* of range or an item could not be removed from
-* the OS.
-*
-* @param index the index of the item
-* @return the selection state
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_ITEM_NOT_REMOVED)
-* when the operation fails
-*/
-public void remove (int index) {
- checkWidget();
- list.remove (index);
-}
-/**
-* Removes a range of items.
-* <p>
-* Indexing is zero based. The range of items
-* is from the start index up to and including
-* the end index.
-*
-* This operation will fail when the index is out
-* of range or an item could not be removed from
-* the OS.
-*
-* @param start the start of the range
-* @param end the end of the range
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_ITEM_NOT_REMOVED)
-* when the operation fails
-*/
-public void remove (int start, int end) {
- checkWidget();
- list.remove (start, end);
-}
-/**
-* Removes an item.
-* <p>
-* This operation will fail when the item
-* could not be removed from the OS.
-*
-* @param string the search item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when string is null
-* @exception SWTError(ERROR_ITEM_NOT_REMOVED)
-* when the operation fails
-*/
-public void remove (String string) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- list.remove (string);
-}
-/**
-* Removes all items.
-* <p>
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public void removeAll () {
- checkWidget();
- text.setText (""); //$NON-NLS-1$
- list.removeAll ();
-}
-/**
-* Removes the listener.
-* <p>
-*
-* @param listener the listener
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when listener is null
-*/
-public void removeModifyListener (ModifyListener listener) {
- checkWidget();
- if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- removeListener(SWT.Modify, listener);
-}
-/**
-* Removes the listener.
-* <p>
-*
-* @param listener the listener
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when listener is null
-*/
-public void removeSelectionListener (SelectionListener listener) {
- checkWidget();
- if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- removeListener(SWT.Selection, listener);
- removeListener(SWT.DefaultSelection,listener);
-}
-/**
-* Selects an item.
-* <p>
-* If the item at an index is not selected, it is
-* selected. Indices that are out of
-* range are ignored. Indexing is zero based.
-*
-* @param index the index of the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public void select (int index) {
- checkWidget();
- if (index == -1) {
- list.deselectAll ();
- text.setText (""); //$NON-NLS-1$
- return;
- }
- if (0 <= index && index < list.getItemCount()) {
- if (index != getSelectionIndex()) {
- text.setText (list.getItem (index));
- text.selectAll ();
- list.select (index);
- list.showSelection ();
- }
- }
-}
-public void setBackground (Color color) {
- super.setBackground(color);
- if (text != null) text.setBackground(color);
- if (list != null) list.setBackground(color);
- if (arrow != null) arrow.setBackground(color);
-}
-public boolean setFocus () {
- checkWidget();
- return text.setFocus ();
-}
-public void setFont (Font font) {
- super.setFont (font);
- text.setFont (font);
- list.setFont (font);
- internalLayout ();
-}
-public void setForeground (Color color) {
- super.setForeground(color);
- if (text != null) text.setForeground(color);
- if (list != null) list.setForeground(color);
- if (arrow != null) arrow.setForeground(color);
-}
-/**
-* Sets the text of an item.
-* <p>
-* Indexing is zero based.
-*
-* This operation will fail when the index is out
-* of range or an item could not be changed in
-* the OS.
-*
-* @param index the index for the item
-* @param string the item
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when items is null
-* @exception SWTError(ERROR_ITEM_NOT_MODIFIED)
-* when the operation fails
-*/
-public void setItem (int index, String string) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- list.setItem (index, string);
-}
-/**
-* Sets all items.
-* <p>
-* @param items the array of items
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when items is null
-* @exception SWTError(ERROR_ITEM_NOT_ADDED)
-* when the operation fails
-*/
-public void setItems (String [] items) {
- checkWidget();
- if (items == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- int style = getStyle();
- if ((style & SWT.READ_ONLY) != 0) text.setText (""); //$NON-NLS-1$
- list.setItems (items);
-}
-/**
-* Sets the new selection.
-* <p>
-* @param selection point representing the start and the end of the new selection
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when selection is null
-*/
-public void setSelection (Point selection) {
- checkWidget();
- if (selection == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- text.setSelection (selection.x, selection.y);
-}
-
-/**
-* Sets the widget text
-* <p>
-* @param string the widget text
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_NULL_ARGUMENT)
-* when string is null
-*/
-public void setText (String string) {
- checkWidget();
- if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- int index = list.indexOf (string);
- if (index == -1) {
- list.deselectAll ();
- text.setText (string);
- return;
- }
- text.setText (string);
- text.selectAll ();
- list.setSelection (index);
- list.showSelection ();
-}
-/**
-* Sets the text limit
-* <p>
-* @param limit new text limit
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-* @exception SWTError(ERROR_CANNOT_BE_ZERO)
-* when limit is 0
-*/
-public void setTextLimit (int limit) {
- checkWidget();
- text.setTextLimit (limit);
-}
-
-public void setToolTipText (String string) {
- checkWidget();
- super.setToolTipText(string);
- arrow.setToolTipText (string);
- text.setToolTipText (string);
-}
-
-public void setVisible (boolean visible) {
- super.setVisible(visible);
- if (!visible) popup.setVisible(false);
-}
-
-void textEvent (Event event) {
- switch (event.type) {
- case SWT.FocusIn: {
- if (hasFocus) return;
- hasFocus = true;
- if (getEditable ()) text.selectAll ();
- Event e = new Event();
- e.time = event.time;
- notifyListeners(SWT.FocusIn, e);
- break;
- }
- case SWT.FocusOut: {
- event.display.asyncExec(new Runnable() {
- public void run() {
- if (CCombo.this.isDisposed()) return;
- Control focusControl = getDisplay().getFocusControl();
- if (focusControl == list || focusControl == arrow) return;
- hasFocus = false;
- Event e = new Event();
- notifyListeners(SWT.FocusOut, e);
- }
- });
- break;
- }
- case SWT.KeyDown: {
-
- if (event.character == SWT.ESC) { // escape key cancels popup list
- dropDown (false);
- }
- if (event.character == SWT.CR) {
- dropDown (false);
- Event e = new Event();
- e.time = event.time;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.DefaultSelection, e);
- }
- //At this point the widget may have been disposed.
- // If so, do not continue.
- if (isDisposed()) break;
-
- if (event.keyCode == SWT.ARROW_UP || event.keyCode == SWT.ARROW_DOWN) {
- int oldIndex = getSelectionIndex ();
- if (event.keyCode == SWT.ARROW_UP) {
- select (Math.max (oldIndex - 1, 0));
- } else {
- select (Math.min (oldIndex + 1, getItemCount () - 1));
- }
-
- if (oldIndex != getSelectionIndex ()) {
- Event e = new Event();
- e.time = event.time;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.Selection, e);
- }
- //At this point the widget may have been disposed.
- // If so, do not continue.
- if (isDisposed()) break;
- }
-
- // Further work : Need to add support for incremental search in
- // pop up list as characters typed in text widget
-
- Event e = new Event();
- e.time = event.time;
- e.character = event.character;
- e.keyCode = event.keyCode;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.KeyDown, e);
- break;
- }
- case SWT.KeyUp: {
- Event e = new Event();
- e.time = event.time;
- e.character = event.character;
- e.keyCode = event.keyCode;
- e.stateMask = event.stateMask;
- notifyListeners(SWT.KeyUp, e);
- break;
- }
- case SWT.Modify: {
- list.deselectAll ();
- Event e = new Event();
- e.time = event.time;
- notifyListeners(SWT.Modify, e);
- break;
- }
- case SWT.MouseDown: {
- if (event.button != 1) return;
- if (text.getEditable ()) return;
- boolean dropped = isDropped ();
- text.selectAll ();
- if (!dropped) setFocus ();
- dropDown (!dropped);
- break;
- }
- case SWT.MouseUp: {
- if (event.button != 1) return;
- if (text.getEditable ()) return;
- text.selectAll ();
- break;
- }
- case SWT.Traverse: {
- switch (event.detail) {
- case SWT.TRAVERSE_RETURN:
- case SWT.TRAVERSE_ARROW_PREVIOUS:
- case SWT.TRAVERSE_ARROW_NEXT:
- // The enter causes default selection and
- // the arrow keys are used to manipulate the list contents so
- // do not use them for traversal.
- event.doit = false;
- break;
- }
-
- Event e = new Event();
- e.time = event.time;
- e.detail = event.detail;
- e.doit = event.doit;
- e.keyCode = event.keyCode;
- notifyListeners(SWT.Traverse, e);
- event.doit = e.doit;
- break;
- }
- }
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.accessibility.*;
+
+/**
+ * The CCombo class represents a selectable user interface object
+ * that combines a text field and a list and issues notificiation
+ * when an item is selected from the list.
+ * <p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add children to it, or set a layout on it.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b>
+ * <dd>BORDER, READ_ONLY, FLAT</dd>
+ * <dt><b>Events:</b>
+ * <dd>Selection</dd>
+ * </dl>
+ */
+public final class CCombo extends Composite {
+
+ static final int ITEMS_SHOWING = 5;
+
+ Text text;
+ List list;
+ Shell popup;
+ Button arrow;
+ boolean hasFocus;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#BORDER
+ * @see SWT#READ_ONLY
+ * @see SWT#FLAT
+ * @see Widget#getStyle
+ */
+public CCombo (Composite parent, int style) {
+ super (parent, checkStyle (style));
+
+ style = getStyle();
+
+ int textStyle = SWT.SINGLE;
+ if ((style & SWT.READ_ONLY) != 0) textStyle |= SWT.READ_ONLY;
+ if ((style & SWT.FLAT) != 0) textStyle |= SWT.FLAT;
+ text = new Text (this, textStyle);
+
+ popup = new Shell (getShell (), SWT.NO_TRIM);
+
+ int listStyle = SWT.SINGLE | SWT.V_SCROLL;
+ if ((style & SWT.FLAT) != 0) listStyle |= SWT.FLAT;
+ list = new List (popup, listStyle);
+
+ int arrowStyle = SWT.ARROW | SWT.DOWN;
+ if ((style & SWT.FLAT) != 0) arrowStyle |= SWT.FLAT;
+ arrow = new Button (this, arrowStyle);
+
+ Listener listener = new Listener () {
+ public void handleEvent (Event event) {
+ if (popup == event.widget) {
+ popupEvent (event);
+ return;
+ }
+ if (text == event.widget) {
+ textEvent (event);
+ return;
+ }
+ if (list == event.widget) {
+ listEvent (event);
+ return;
+ }
+ if (arrow == event.widget) {
+ arrowEvent (event);
+ return;
+ }
+ if (CCombo.this == event.widget) {
+ comboEvent (event);
+ return;
+ }
+
+ }
+ };
+
+ int [] comboEvents = {SWT.Dispose, SWT.Move, SWT.Resize};
+ for (int i=0; i<comboEvents.length; i++) this.addListener (comboEvents [i], listener);
+
+ int [] popupEvents = {SWT.Close, SWT.Paint, SWT.Deactivate};
+ for (int i=0; i<popupEvents.length; i++) popup.addListener (popupEvents [i], listener);
+
+ int [] textEvents = {SWT.KeyDown, SWT.KeyUp, SWT.Modify, SWT.MouseDown, SWT.MouseUp, SWT.Traverse, SWT.FocusIn, SWT.FocusOut};
+ for (int i=0; i<textEvents.length; i++) text.addListener (textEvents [i], listener);
+
+ int [] listEvents = {SWT.MouseUp, SWT.Selection, SWT.Traverse, SWT.KeyDown, SWT.KeyUp, SWT.FocusIn, SWT.FocusOut};
+ for (int i=0; i<listEvents.length; i++) list.addListener (listEvents [i], listener);
+
+ int [] arrowEvents = {SWT.Selection, SWT.FocusIn, SWT.FocusOut};
+ for (int i=0; i<arrowEvents.length; i++) arrow.addListener (arrowEvents [i], listener);
+
+ initAccessible();
+}
+static int checkStyle (int style) {
+ int mask = SWT.BORDER | SWT.READ_ONLY | SWT.FLAT;
+ return style & mask;
+}
+/**
+* Adds an item.
+* <p>
+* The item is placed at the end of the list.
+* Indexing is zero based.
+*
+* @param string the new item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when the string is null
+* @exception SWTError(ERROR_ITEM_NOT_ADDED)
+* when the item cannot be added
+*/
+public void add (String string) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ list.add (string);
+}
+/**
+* Adds an item at an index.
+* <p>
+* The item is placed at an index in the list.
+* Indexing is zero based.
+*
+* This operation will fail when the index is
+* out of range.
+*
+* @param string the new item
+* @param index the index for the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when the string is null
+* @exception SWTError(ERROR_ITEM_NOT_ADDED)
+* when the item cannot be added
+*/
+public void add (String string, int index) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ list.add (string, index);
+}
+/**
+* Adds the listener to receive events.
+* <p>
+*
+* @param listener the listener
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when listener is null
+*/
+public void addModifyListener (ModifyListener listener) {;
+ checkWidget();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Modify, typedListener);
+}
+/**
+* Adds the listener to receive events.
+* <p>
+*
+* @param listener the listener
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when listener is null
+*/
+public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Selection,typedListener);
+ addListener (SWT.DefaultSelection,typedListener);
+}
+void arrowEvent (Event event) {
+ switch (event.type) {
+ case SWT.FocusIn: {
+ if (hasFocus) return;
+ hasFocus = true;
+ if (getEditable ()) text.selectAll ();
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusIn, e);
+ break;
+ }
+ case SWT.FocusOut: {
+ event.display.asyncExec(new Runnable() {
+ public void run() {
+ if (CCombo.this.isDisposed()) return;
+ Control focusControl = getDisplay().getFocusControl();
+ if (focusControl == list || focusControl == text) return;
+ hasFocus = false;
+ Event e = new Event();
+ notifyListeners(SWT.FocusOut, e);
+ }
+ });
+ break;
+ }
+ case SWT.Selection: {
+ dropDown (!isDropped ());
+ break;
+ }
+ }
+}
+/**
+* Clears the current selection.
+* <p>
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public void clearSelection () {
+ checkWidget();
+ text.clearSelection ();
+ list.deselectAll ();
+}
+void comboEvent (Event event) {
+ switch (event.type) {
+ case SWT.Dispose:
+ if (popup != null && !popup.isDisposed ()) popup.dispose ();
+ popup = null;
+ text = null;
+ list = null;
+ arrow = null;
+ break;
+ case SWT.Move:
+ dropDown(false);
+ break;
+ case SWT.Resize:
+ internalLayout();
+ break;
+ }
+}
+
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget();
+ int width = 0, height = 0;
+ Point textSize = text.computeSize (wHint, SWT.DEFAULT, changed);
+ Point arrowSize = arrow.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
+ Point listSize = list.computeSize (wHint, SWT.DEFAULT, changed);
+ int borderWidth = getBorderWidth();
+
+ height = Math.max (hHint, Math.max(textSize.y, arrowSize.y) + 2*borderWidth);
+ width = Math.max (wHint, Math.max(textSize.x + arrowSize.x + 2*borderWidth, listSize.x + 2) );
+ return new Point (width, height);
+}
+/**
+* Deselects an item.
+* <p>
+* If the item at an index is selected, it is
+* deselected. If the item at an index is not
+* selected, it remains deselected. Indices
+* that are out of range are ignored. Indexing
+* is zero based.
+*
+* @param index the index of the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public void deselect (int index) {
+ checkWidget();
+ list.deselect (index);
+}
+/**
+* Deselects all items.
+* <p>
+*
+* If an item is selected, it is deselected.
+* If an item is not selected, it remains unselected.
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public void deselectAll () {
+ checkWidget();
+ list.deselectAll ();
+}
+void dropDown (boolean drop) {
+ if (drop == isDropped ()) return;
+ if (!drop) {
+ popup.setVisible (false);
+ text.setFocus();
+ return;
+ }
+ int index = list.getSelectionIndex ();
+ if (index != -1) list.setTopIndex (index);
+ Rectangle listRect = list.getBounds ();
+ Point point = getParent().toDisplay (getLocation ());
+ Point comboSize = getSize();
+ int width = Math.max (comboSize.x, listRect.width + 2);
+ popup.setBounds (point.x, point.y + comboSize.y, width, listRect.height + 2);
+ popup.setVisible (true);
+ list.setFocus();
+}
+public Control [] getChildren () {
+ checkWidget();
+ return new Control [0];
+}
+boolean getEditable () {
+ return text.getEditable ();
+}
+/**
+* Gets an item at an index.
+* <p>
+* Indexing is zero based.
+*
+* This operation will fail when the index is out
+* of range or an item could not be queried from
+* the OS.
+*
+* @param index the index of the item
+* @return the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_CANNOT_GET_ITEM)
+* when the operation fails
+*/
+public String getItem (int index) {
+ checkWidget();
+ return list.getItem (index);
+}
+/**
+* Gets the number of items.
+* <p>
+* This operation will fail if the number of
+* items could not be queried from the OS.
+*
+* @return the number of items in the widget
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_CANNOT_GET_COUNT)
+* when the operation fails
+*/
+public int getItemCount () {
+ checkWidget();
+ return list.getItemCount ();
+}
+/**
+* Gets the height of one item.
+* <p>
+* This operation will fail if the height of
+* one item could not be queried from the OS.
+*
+* @return the height of one item in the widget
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_CANNOT_GET_ITEM_HEIGHT)
+* when the operation fails
+*/
+public int getItemHeight () {
+ checkWidget();
+ return list.getItemHeight ();
+}
+/**
+* Gets the items.
+* <p>
+* This operation will fail if the items cannot
+* be queried from the OS.
+*
+* @return the items in the widget
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_CANNOT_GET_ITEM)
+* when the operation fails
+*/
+public String [] getItems () {
+ checkWidget();
+ return list.getItems ();
+}
+/**
+* Gets the selection.
+* <p>
+* @return a point representing the selection start and end
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public Point getSelection () {
+ checkWidget();
+ return text.getSelection ();
+}
+/**
+* Gets the index of the selected item.
+* <p>
+* Indexing is zero based.
+* If no item is selected -1 is returned.
+*
+* @return the index of the selected item.
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public int getSelectionIndex () {
+ checkWidget();
+ return list.getSelectionIndex ();
+}
+/**
+* Gets the widget text.
+* <p>
+* If the widget has no text, an empty string is returned.
+*
+* @return the widget text
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public String getText () {
+ checkWidget();
+ return text.getText ();
+}
+/**
+* Gets the height of the combo's text field.
+* <p>
+* The operation will fail if the height cannot
+* be queried from the OS.
+
+* @return the height of the combo's text field.
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_ERROR_CANNOT_GET_ITEM_HEIGHT)
+* when the operation fails
+*/
+public int getTextHeight () {
+ checkWidget();
+ return text.getLineHeight();
+}
+/**
+* Gets the text limit.
+* <p>
+* @return the text limit
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public int getTextLimit () {
+ checkWidget();
+ return text.getTextLimit ();
+}
+/**
+* Gets the index of an item.
+* <p>
+* The list is searched starting at 0 until an
+* item is found that is equal to the search item.
+* If no item is found, -1 is returned. Indexing
+* is zero based.
+*
+* @param string the search item
+* @return the index of the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when string is null
+*/
+public int indexOf (String string) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return list.indexOf (string);
+}
+/**
+* Gets the index of an item.
+* <p>
+* The widget is searched starting at start including
+* the end position until an item is found that
+* is equal to the search itenm. If no item is
+* found, -1 is returned. Indexing is zero based.
+*
+* @param string the search item
+* @param index the starting position
+* @return the index of the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when string is null
+*/
+public int indexOf (String string, int start) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return list.indexOf (string, start);
+}
+
+void initAccessible() {
+ getAccessible().addAccessibleListener(new AccessibleAdapter() {
+ public void getHelp(AccessibleEvent e) {
+ e.result = getToolTipText();
+ }
+ });
+
+ getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() {
+ public void getChildAtPoint(AccessibleControlEvent e) {
+ Point testPoint = toControl(new Point(e.x, e.y));
+ if (getBounds().contains(testPoint)) {
+ e.childID = ACC.CHILDID_SELF;
+ }
+ }
+
+ public void getLocation(AccessibleControlEvent e) {
+ Rectangle location = getBounds();
+ Point pt = toDisplay(new Point(location.x, location.y));
+ e.x = pt.x;
+ e.y = pt.y;
+ e.width = location.width;
+ e.height = location.height;
+ }
+
+ public void getChildCount(AccessibleControlEvent e) {
+ e.detail = 0;
+ }
+
+ public void getRole(AccessibleControlEvent e) {
+ e.detail = ACC.ROLE_COMBOBOX;
+ }
+
+ public void getState(AccessibleControlEvent e) {
+ e.detail = ACC.STATE_NORMAL;
+ }
+
+ public void getValue(AccessibleControlEvent e) {
+ e.result = getText();
+ }
+ });
+}
+boolean isDropped () {
+ return popup.getVisible ();
+}
+public boolean isFocusControl () {
+ checkWidget();
+ if (text.isFocusControl() || arrow.isFocusControl() || list.isFocusControl() || popup.isFocusControl()) {
+ return true;
+ } else {
+ return super.isFocusControl();
+ }
+}
+void internalLayout () {
+ if (isDropped ()) dropDown (false);
+
+ Rectangle rect = getClientArea();
+ int width = rect.width;
+ int height = rect.height;
+ Point arrowSize = arrow.computeSize(SWT.DEFAULT, height);
+ text.setBounds (0, 0, width - arrowSize.x, height);
+ arrow.setBounds (width - arrowSize.x, 0, arrowSize.x, arrowSize.y);
+
+ Point size = getSize();
+ int itemHeight = list.getItemHeight () * ITEMS_SHOWING;
+ Point listSize = list.computeSize (SWT.DEFAULT, itemHeight);
+ list.setBounds (1, 1, Math.max (size.x - 2, listSize.x), listSize.y);
+}
+void listEvent (Event event) {
+ switch (event.type) {
+ case SWT.FocusIn: {
+ if (hasFocus) return;
+ hasFocus = true;
+ if (getEditable ()) text.selectAll ();
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusIn, e);
+ break;
+ }
+ case SWT.FocusOut: {
+ event.display.asyncExec(new Runnable() {
+ public void run() {
+ if (CCombo.this.isDisposed()) return;
+ Control focusControl = getDisplay().getFocusControl();
+ if (focusControl == text || focusControl == arrow) return;
+ hasFocus = false;
+ Event e = new Event();
+ notifyListeners(SWT.FocusOut, e);
+ }
+ });
+ break;
+ }
+ case SWT.MouseUp: {
+ if (event.button != 1) return;
+ dropDown (false);
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.DefaultSelection, e);
+ break;
+ }
+ case SWT.Selection: {
+ int index = list.getSelectionIndex ();
+ if (index == -1) return;
+ text.setText (list.getItem (index));
+ text.selectAll ();
+ list.setSelection(index);
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ e.doit = event.doit;
+ notifyListeners(SWT.Selection, e);
+ event.doit = e.doit;
+ break;
+ }
+ case SWT.Traverse: {
+ switch (event.detail) {
+ case SWT.TRAVERSE_TAB_NEXT:
+ case SWT.TRAVERSE_RETURN:
+ case SWT.TRAVERSE_ESCAPE:
+ case SWT.TRAVERSE_ARROW_PREVIOUS:
+ case SWT.TRAVERSE_ARROW_NEXT:
+ event.doit = false;
+ break;
+ }
+ Event e = new Event();
+ e.time = event.time;
+ e.detail = event.detail;
+ e.doit = event.doit;
+ e.keyCode = event.keyCode;
+ notifyListeners(SWT.Traverse, e);
+ event.doit = e.doit;
+ break;
+ }
+ case SWT.KeyUp: {
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyUp, e);
+ break;
+ }
+ case SWT.KeyDown: {
+ if (event.character == SWT.ESC) {
+ // escape key cancels popup list
+ dropDown (false);
+ }
+ if (event.character == SWT.CR || event.character == '\t') {
+ // Enter and Tab cause default selection
+ dropDown (false);
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.DefaultSelection, e);
+ }
+ //At this point the widget may have been disposed.
+ // If so, do not continue.
+ if (isDisposed()) break;
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyDown, e);
+ break;
+
+ }
+ }
+}
+void popupEvent(Event event) {
+ switch (event.type) {
+ case SWT.Paint:
+ // draw black rectangle around list
+ Rectangle listRect = list.getBounds();
+ Color black = getDisplay().getSystemColor(SWT.COLOR_BLACK);
+ event.gc.setForeground(black);
+ event.gc.drawRectangle(0, 0, listRect.width + 1, listRect.height + 1);
+ break;
+ case SWT.Close:
+ event.doit = false;
+ dropDown (false);
+ break;
+ case SWT.Deactivate:
+ dropDown (false);
+ break;
+ }
+}
+public void redraw (int x, int y, int width, int height, boolean all) {
+ checkWidget();
+ if (!all) return;
+ Point location = text.getLocation();
+ text.redraw(x - location.x, y - location.y, width, height, all);
+ location = list.getLocation();
+ list.redraw(x - location.x, y - location.y, width, height, all);
+ if (arrow != null) {
+ location = arrow.getLocation();
+ arrow.redraw(x - location.x, y - location.y, width, height, all);
+ }
+}
+
+/**
+* Removes an item at an index.
+* <p>
+* Indexing is zero based.
+*
+* This operation will fail when the index is out
+* of range or an item could not be removed from
+* the OS.
+*
+* @param index the index of the item
+* @return the selection state
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_ITEM_NOT_REMOVED)
+* when the operation fails
+*/
+public void remove (int index) {
+ checkWidget();
+ list.remove (index);
+}
+/**
+* Removes a range of items.
+* <p>
+* Indexing is zero based. The range of items
+* is from the start index up to and including
+* the end index.
+*
+* This operation will fail when the index is out
+* of range or an item could not be removed from
+* the OS.
+*
+* @param start the start of the range
+* @param end the end of the range
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_ITEM_NOT_REMOVED)
+* when the operation fails
+*/
+public void remove (int start, int end) {
+ checkWidget();
+ list.remove (start, end);
+}
+/**
+* Removes an item.
+* <p>
+* This operation will fail when the item
+* could not be removed from the OS.
+*
+* @param string the search item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when string is null
+* @exception SWTError(ERROR_ITEM_NOT_REMOVED)
+* when the operation fails
+*/
+public void remove (String string) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ list.remove (string);
+}
+/**
+* Removes all items.
+* <p>
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public void removeAll () {
+ checkWidget();
+ text.setText (""); //$NON-NLS-1$
+ list.removeAll ();
+}
+/**
+* Removes the listener.
+* <p>
+*
+* @param listener the listener
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when listener is null
+*/
+public void removeModifyListener (ModifyListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener(SWT.Modify, listener);
+}
+/**
+* Removes the listener.
+* <p>
+*
+* @param listener the listener
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when listener is null
+*/
+public void removeSelectionListener (SelectionListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener(SWT.Selection, listener);
+ removeListener(SWT.DefaultSelection,listener);
+}
+/**
+* Selects an item.
+* <p>
+* If the item at an index is not selected, it is
+* selected. Indices that are out of
+* range are ignored. Indexing is zero based.
+*
+* @param index the index of the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public void select (int index) {
+ checkWidget();
+ if (index == -1) {
+ list.deselectAll ();
+ text.setText (""); //$NON-NLS-1$
+ return;
+ }
+ if (0 <= index && index < list.getItemCount()) {
+ if (index != getSelectionIndex()) {
+ text.setText (list.getItem (index));
+ text.selectAll ();
+ list.select (index);
+ list.showSelection ();
+ }
+ }
+}
+public void setBackground (Color color) {
+ super.setBackground(color);
+ if (text != null) text.setBackground(color);
+ if (list != null) list.setBackground(color);
+ if (arrow != null) arrow.setBackground(color);
+}
+public boolean setFocus () {
+ checkWidget();
+ return text.setFocus ();
+}
+public void setFont (Font font) {
+ super.setFont (font);
+ text.setFont (font);
+ list.setFont (font);
+ internalLayout ();
+}
+public void setForeground (Color color) {
+ super.setForeground(color);
+ if (text != null) text.setForeground(color);
+ if (list != null) list.setForeground(color);
+ if (arrow != null) arrow.setForeground(color);
+}
+/**
+* Sets the text of an item.
+* <p>
+* Indexing is zero based.
+*
+* This operation will fail when the index is out
+* of range or an item could not be changed in
+* the OS.
+*
+* @param index the index for the item
+* @param string the item
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when items is null
+* @exception SWTError(ERROR_ITEM_NOT_MODIFIED)
+* when the operation fails
+*/
+public void setItem (int index, String string) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ list.setItem (index, string);
+}
+/**
+* Sets all items.
+* <p>
+* @param items the array of items
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when items is null
+* @exception SWTError(ERROR_ITEM_NOT_ADDED)
+* when the operation fails
+*/
+public void setItems (String [] items) {
+ checkWidget();
+ if (items == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ int style = getStyle();
+ if ((style & SWT.READ_ONLY) != 0) text.setText (""); //$NON-NLS-1$
+ list.setItems (items);
+}
+/**
+* Sets the new selection.
+* <p>
+* @param selection point representing the start and the end of the new selection
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when selection is null
+*/
+public void setSelection (Point selection) {
+ checkWidget();
+ if (selection == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ text.setSelection (selection.x, selection.y);
+}
+
+/**
+* Sets the widget text
+* <p>
+* @param string the widget text
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_NULL_ARGUMENT)
+* when string is null
+*/
+public void setText (String string) {
+ checkWidget();
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ int index = list.indexOf (string);
+ if (index == -1) {
+ list.deselectAll ();
+ text.setText (string);
+ return;
+ }
+ text.setText (string);
+ text.selectAll ();
+ list.setSelection (index);
+ list.showSelection ();
+}
+/**
+* Sets the text limit
+* <p>
+* @param limit new text limit
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+* @exception SWTError(ERROR_CANNOT_BE_ZERO)
+* when limit is 0
+*/
+public void setTextLimit (int limit) {
+ checkWidget();
+ text.setTextLimit (limit);
+}
+
+public void setToolTipText (String string) {
+ checkWidget();
+ super.setToolTipText(string);
+ arrow.setToolTipText (string);
+ text.setToolTipText (string);
+}
+
+public void setVisible (boolean visible) {
+ super.setVisible(visible);
+ if (!visible) popup.setVisible(false);
+}
+
+void textEvent (Event event) {
+ switch (event.type) {
+ case SWT.FocusIn: {
+ if (hasFocus) return;
+ hasFocus = true;
+ if (getEditable ()) text.selectAll ();
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusIn, e);
+ break;
+ }
+ case SWT.FocusOut: {
+ event.display.asyncExec(new Runnable() {
+ public void run() {
+ if (CCombo.this.isDisposed()) return;
+ Control focusControl = getDisplay().getFocusControl();
+ if (focusControl == list || focusControl == arrow) return;
+ hasFocus = false;
+ Event e = new Event();
+ notifyListeners(SWT.FocusOut, e);
+ }
+ });
+ break;
+ }
+ case SWT.KeyDown: {
+
+ if (event.character == SWT.ESC) { // escape key cancels popup list
+ dropDown (false);
+ }
+ if (event.character == SWT.CR) {
+ dropDown (false);
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.DefaultSelection, e);
+ }
+ //At this point the widget may have been disposed.
+ // If so, do not continue.
+ if (isDisposed()) break;
+
+ if (event.keyCode == SWT.ARROW_UP || event.keyCode == SWT.ARROW_DOWN) {
+ int oldIndex = getSelectionIndex ();
+ if (event.keyCode == SWT.ARROW_UP) {
+ select (Math.max (oldIndex - 1, 0));
+ } else {
+ select (Math.min (oldIndex + 1, getItemCount () - 1));
+ }
+
+ if (oldIndex != getSelectionIndex ()) {
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.Selection, e);
+ }
+ //At this point the widget may have been disposed.
+ // If so, do not continue.
+ if (isDisposed()) break;
+ }
+
+ // Further work : Need to add support for incremental search in
+ // pop up list as characters typed in text widget
+
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyDown, e);
+ break;
+ }
+ case SWT.KeyUp: {
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.KeyUp, e);
+ break;
+ }
+ case SWT.Modify: {
+ list.deselectAll ();
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.Modify, e);
+ break;
+ }
+ case SWT.MouseDown: {
+ if (event.button != 1) return;
+ if (text.getEditable ()) return;
+ boolean dropped = isDropped ();
+ text.selectAll ();
+ if (!dropped) setFocus ();
+ dropDown (!dropped);
+ break;
+ }
+ case SWT.MouseUp: {
+ if (event.button != 1) return;
+ if (text.getEditable ()) return;
+ text.selectAll ();
+ break;
+ }
+ case SWT.Traverse: {
+ switch (event.detail) {
+ case SWT.TRAVERSE_RETURN:
+ case SWT.TRAVERSE_ARROW_PREVIOUS:
+ case SWT.TRAVERSE_ARROW_NEXT:
+ // The enter causes default selection and
+ // the arrow keys are used to manipulate the list contents so
+ // do not use them for traversal.
+ event.doit = false;
+ break;
+ }
+
+ Event e = new Event();
+ e.time = event.time;
+ e.detail = event.detail;
+ e.doit = event.doit;
+ e.keyCode = event.keyCode;
+ notifyListeners(SWT.Traverse, e);
+ event.doit = e.doit;
+ break;
+ }
+ }
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CLabel.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CLabel.java
index eba4c43e10..62576ceb0a 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CLabel.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CLabel.java
@@ -1,611 +1,611 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.accessibility.*;
-
-/**
- * A Label which supports aligned text and/or an image and different border styles.
- * <p>
- * If there is not enough space a CLabel uses the following strategy to fit the
- * information into the available space:
- * <pre>
- * ignores the indent in left align mode
- * ignores the image and the gap
- * shortens the text by replacing the center portion of the label with an ellipsis
- * shortens the text by removing the center portion of the label
- * </pre>
- * <p>
- * <dl>
- * <dt><b>Styles:</b>
- * <dd>LEFT, RIGHT, CENTER, SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
- * <dt><b>Events:</b>
- * <dd></dd>
- * </dl>
- */
-public class CLabel extends Canvas {
-
- /** Gap between icon and text */
- private static final int GAP = 5;
- /** Left and right margins */
- private static final int INDENT = 3;
- /** a string inserted in the middle of text that has been shortened */
- private static final String ellipsis = "..."; //$NON-NLS-1$
- /** the alignment. Either CENTER, RIGHT, LEFT. Default is LEFT*/
- private int align = SWT.LEFT;
- private int hIndent = INDENT;
- private int vIndent = INDENT;
- /** the current text */
- private String text;
- /** the current icon */
- private Image image;
- // The tooltip is used for two purposes - the application can set
- // a tooltip or the tooltip can be used to display the full text when the
- // the text has been truncated due to the label being too short.
- // The appToolTip stores the tooltip set by the application. Control.tooltiptext
- // contains whatever tooltip is currently being displayed.
- private String appToolTipText;
-
- private Image backgroundImage;
- private Color[] gradientColors;
- private int[] gradientPercents;
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#LEFT
- * @see SWT#RIGHT
- * @see SWT#CENTER
- * @see SWT#SHADOW_IN
- * @see SWT#SHADOW_OUT
- * @see SWT#SHADOW_NONE
- * @see #getStyle
- */
-public CLabel(Composite parent, int style) {
- super(parent, checkStyle(style));
-
- if ((style & SWT.CENTER) != 0) align = SWT.CENTER;
- if ((style & SWT.RIGHT) != 0) align = SWT.RIGHT;
- if ((style & SWT.LEFT) != 0) align = SWT.LEFT;
-
- addPaintListener(new PaintListener(){
- public void paintControl(PaintEvent event) {
- onPaint(event);
- }
- });
-
- addDisposeListener(new DisposeListener(){
- public void widgetDisposed(DisposeEvent event) {
- onDispose(event);
- }
- });
-
- initAccessible();
-
-}
-/**
- * Check the style bits to ensure that no invalid styles are applied.
- */
-private static int checkStyle (int style) {
- int mask = SWT.SHADOW_IN | SWT.SHADOW_OUT | SWT.SHADOW_NONE;
- style = style & mask;
- style |= SWT.NO_FOCUS;
- //TEMPORARY CODE
- /*
- * The default background on carbon and some GTK themes is not a solid color
- * but a texture. To show the correct default background, we must allow
- * the operating system to draw it and therefore, we can not use the
- * NO_BACKGROUND style. The NO_BACKGROUND style is not required on platforms
- * that use double buffering which is true in both of these cases.
- */
- String platform = SWT.getPlatform();
- if ("carbon".equals(platform) || "gtk".equals(platform)) return style; //$NON-NLS-1$ //$NON-NLS-2$
- return style | SWT.NO_BACKGROUND;
-}
-public Point computeSize(int wHint, int hHint, boolean changed) {
- checkWidget();
- Point e = getTotalSize(image, text);
- if (wHint == SWT.DEFAULT){
- e.x += 2*hIndent;
- } else {
- e.x = wHint;
- }
- if (hHint == SWT.DEFAULT) {
- e.y += 2*vIndent;
- } else {
- e.y = hHint;
- }
- return e;
-}
-/**
- * Draw a rectangle in the given colors.
- */
-private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
- gc.setForeground(bottomright);
- gc.drawLine(x+w, y, x+w, y+h);
- gc.drawLine(x, y+h, x+w, y+h);
-
- gc.setForeground(topleft);
- gc.drawLine(x, y, x+w-1, y);
- gc.drawLine(x, y, x, y+h-1);
-}
-/**
- * Returns the alignment.
- * The alignment style (LEFT, CENTER or RIGHT) is returned.
- *
- * @return SWT.LEFT, SWT.RIGHT or SWT.CENTER
- */
-public int getAlignment() {
- //checkWidget();
- return align;
-}
-/**
- * Return the CLabel's image or <code>null</code>.
- *
- * @return the image of the label or null
- */
-public Image getImage() {
- //checkWidget();
- return image;
-}
-/**
- * Compute the minimum size.
- */
-private Point getTotalSize(Image image, String text) {
- Point size = new Point(0, 0);
-
- if (image != null) {
- Rectangle r = image.getBounds();
- size.x += r.width;
- size.y += r.height;
- }
-
- GC gc = new GC(this);
- if (text != null && text.length() > 0) {
- Point e = gc.textExtent(text);
- size.x += e.x;
- size.y = Math.max(size.y, e.y);
- if (image != null) size.x += GAP;
- } else {
- size.y = Math.max(size.y, gc.getFontMetrics().getHeight());
- }
- gc.dispose();
-
- return size;
-}
-public void setToolTipText (String string) {
- super.setToolTipText (string);
- appToolTipText = super.getToolTipText();
-}
-/**
- * Return the Label's text.
- *
- * @return the text of the label or null
- */
-public String getText() {
- //checkWidget();
- return text;
-}
-public String getToolTipText () {
- checkWidget();
- return appToolTipText;
-}
-/**
- * Paint the Label's border.
- */
-private void paintBorder(GC gc, Rectangle r) {
- Display disp= getDisplay();
-
- Color c1 = null;
- Color c2 = null;
-
- int style = getStyle();
- if ((style & SWT.SHADOW_IN) != 0) {
- c1 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
- c2 = disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
- }
- if ((style & SWT.SHADOW_OUT) != 0) {
- c1 = disp.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
- c2 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
- }
-
- if (c1 != null && c2 != null) {
- gc.setLineWidth(1);
- drawBevelRect(gc, r.x, r.y, r.width-1, r.height-1, c1, c2);
- }
-}
-private void initAccessible() {
- Accessible accessible = getAccessible();
- accessible.addAccessibleListener(new AccessibleAdapter() {
- public void getName(AccessibleEvent e) {
- e.result = getText();
- }
-
- public void getHelp(AccessibleEvent e) {
- e.result = getToolTipText();
- }
- });
-
- accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
- public void getChildAtPoint(AccessibleControlEvent e) {
- Point pt = toControl(new Point(e.x, e.y));
- e.childID = (getBounds().contains(pt)) ? ACC.CHILDID_SELF : ACC.CHILDID_NONE;
- }
-
- public void getLocation(AccessibleControlEvent e) {
- Rectangle location = getBounds();
- Point pt = toDisplay(new Point(location.x, location.y));
- e.x = pt.x;
- e.y = pt.y;
- e.width = location.width;
- e.height = location.height;
- }
-
- public void getChildCount(AccessibleControlEvent e) {
- e.detail = 0;
- }
-
- public void getRole(AccessibleControlEvent e) {
- e.detail = ACC.ROLE_LABEL;
- }
-
- public void getState(AccessibleControlEvent e) {
- e.detail = ACC.STATE_READONLY;
- }
- });
-}
-private void onDispose(DisposeEvent event) {
- gradientColors = null;
- gradientPercents = null;
- backgroundImage = null;
- text = null;
- image = null;
- appToolTipText = null;
-}
-/*
- * Process the paint event
- */
-private void onPaint(PaintEvent event) {
- Rectangle rect = getClientArea();
- if (rect.width == 0 || rect.height == 0) return;
-
- boolean shortenText = false;
- String t = text;
- Image img = image;
- int availableWidth = rect.width - 2*hIndent;
- Point extent = getTotalSize(img, t);
- if (extent.x > availableWidth) {
- img = null;
- extent = getTotalSize(img, t);
- if (extent.x > availableWidth) {
- shortenText = true;
- }
- }
-
- GC gc = event.gc;
-
- // shorten the text
- if (shortenText) {
- t = shortenText(gc, text, availableWidth);
- extent = getTotalSize(img, t);
- if (appToolTipText == null) {
- super.setToolTipText(text);
- }
- } else {
- super.setToolTipText(appToolTipText);
- }
-
- // determine horizontal position
- int x = rect.x + hIndent;
- if (align == SWT.CENTER) {
- x = (rect.width-extent.x)/2;
- }
- if (align == SWT.RIGHT) {
- x = rect.width-extent.x - hIndent;
- }
-
- // draw a background image behind the text
- try {
- if (backgroundImage != null) {
- // draw a background image behind the text
- Rectangle imageRect = backgroundImage.getBounds();
- gc.drawImage(backgroundImage, 0, 0, imageRect.width, imageRect.height,
- 0, 0, rect.width, rect.height);
- } else if (gradientColors != null) {
- // draw a gradient behind the text
- final Color oldBackground = gc.getBackground();
- if (gradientColors.length == 1) {
- if (gradientColors[0] != null) gc.setBackground(gradientColors[0]);
- gc.fillRectangle(0, 0, rect.width, rect.height);
- } else {
- final Color oldForeground = gc.getForeground();
- Color lastColor = gradientColors[0];
- if (lastColor == null) lastColor = oldBackground;
- for (int i = 0, pos = 0; i < gradientPercents.length; ++i) {
- gc.setForeground(lastColor);
- lastColor = gradientColors[i + 1];
- if (lastColor == null) lastColor = oldBackground;
- gc.setBackground(lastColor);
- final int gradientWidth = (gradientPercents[i] * rect.width / 100) - pos;
- gc.fillGradientRectangle(pos, 0, gradientWidth, rect.height, false);
- pos += gradientWidth;
- }
- gc.setForeground(oldForeground);
- }
- gc.setBackground(oldBackground);
- } else {
- if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
- gc.setBackground(getBackground());
- gc.fillRectangle(rect);
- }
- }
- } catch (SWTException e) {
- if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
- gc.setBackground(getBackground());
- gc.fillRectangle(rect);
- }
- }
-
- // draw border
- int style = getStyle();
- if ((style & SWT.SHADOW_IN) != 0 || (style & SWT.SHADOW_OUT) != 0) {
- paintBorder(gc, rect);
- }
- // draw the image
- if (img != null) {
- Rectangle imageRect = img.getBounds();
- gc.drawImage(img, 0, 0, imageRect.width, imageRect.height,
- x, (rect.height-imageRect.height)/2, imageRect.width, imageRect.height);
- x += imageRect.width + GAP;
- }
- // draw the text
- if (t != null) {
- int textHeight = gc.getFontMetrics().getHeight();
- gc.setForeground(getForeground());
- gc.drawText(t, x, rect.y + (rect.height-textHeight)/2, true);
- }
-}
-/**
- * Set the alignment of the CLabel.
- * Use the values LEFT, CENTER and RIGHT to align image and text within the available space.
- *
- * @param align the alignment style of LEFT, RIGHT or CENTER
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - if the value of align is not one of SWT.LEFT, SWT.RIGHT or SWT.CENTER</li>
- * </ul>
- */
-public void setAlignment(int align) {
- checkWidget();
- if (align != SWT.LEFT && align != SWT.RIGHT && align != SWT.CENTER) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (this.align != align) {
- this.align = align;
- redraw();
- }
-}
-
-public void setBackground (Color color) {
- super.setBackground (color);
- // Are these settings the same as before?
- if (color != null && backgroundImage == null &&
- gradientColors == null && gradientPercents == null) {
- Color background = getBackground();
- if (color.equals(background)) {
- return;
- }
- }
- backgroundImage = null;
- gradientColors = null;
- gradientPercents = null;
- redraw ();
-}
-
-/**
- * Specify a gradient of colours to be drawn in the background of the CLabel.
- * <p>For example, to draw a gradient that varies from dark blue to blue and then to
- * white and stays white for the right hald of the label, use the following call
- * to setBackground:</p>
- * <pre>
- * clabel.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
- * display.getSystemColor(SWT.COLOR_BLUE),
- * display.getSystemColor(SWT.COLOR_WHITE),
- * display.getSystemColor(SWT.COLOR_WHITE)},
- * new int[] {25, 50, 100});
- * </pre>
- *
- * @param colors an array of Color that specifies the colors to appear in the gradient
- * in order of appearance from left to right; The value <code>null</code>
- * clears the background gradient; the value <code>null</code> can be used
- * inside the array of Color to specify the background color.
- * @param percents an array of integers between 0 and 100 specifying the percent of the width
- * of the widget at which the color should change; the size of the percents
- * array must be one less than the size of the colors array.
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - if the values of colors and percents are not consistant</li>
- * </ul>
- */
-public void setBackground(Color[] colors, int[] percents) {
- checkWidget();
- if (colors != null) {
- if (percents == null || percents.length != colors.length - 1) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (getDisplay().getDepth() < 15) {
- // Don't use gradients on low color displays
- colors = new Color[] { colors[0] };
- percents = new int[] { };
- }
- for (int i = 0; i < percents.length; i++) {
- if (percents[i] < 0 || percents[i] > 100) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (i > 0 && percents[i] < percents[i-1]) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- }
- }
-
- // Are these settings the same as before?
- final Color background = getBackground();
- if (backgroundImage == null) {
- if ((gradientColors != null) && (colors != null) &&
- (gradientColors.length == colors.length)) {
- boolean same = false;
- for (int i = 0; i < gradientColors.length; i++) {
- same = (gradientColors[i] == colors[i]) ||
- ((gradientColors[i] == null) && (colors[i] == background)) ||
- ((gradientColors[i] == background) && (colors[i] == null));
- if (!same) break;
- }
- if (same) {
- for (int i = 0; i < gradientPercents.length; i++) {
- same = gradientPercents[i] == percents[i];
- if (!same) break;
- }
- }
- if (same) return;
- }
- } else {
- backgroundImage = null;
- }
- // Store the new settings
- if (colors == null) {
- gradientColors = null;
- gradientPercents = null;
- } else {
- gradientColors = new Color[colors.length];
- for (int i = 0; i < colors.length; ++i)
- gradientColors[i] = (colors[i] != null) ? colors[i] : background;
- gradientPercents = new int[percents.length];
- for (int i = 0; i < percents.length; ++i)
- gradientPercents[i] = percents[i];
- }
- // Refresh with the new settings
- redraw();
-}
-/**
- * Set the image to be drawn in the background of the label.
- *
- * @param image the image to be drawn in the background
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setBackground(Image image) {
- checkWidget();
- if (image == backgroundImage) return;
- if (image != null) {
- gradientColors = null;
- gradientPercents = null;
- }
- backgroundImage = image;
- redraw();
-
-}
-public void setFont(Font font) {
- super.setFont(font);
- redraw();
-}
-/**
- * Set the label's Image.
- * The value <code>null</code> clears it.
- *
- * @param image the image to be displayed in the label or null
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setImage(Image image) {
- checkWidget();
- if (image != this.image) {
- this.image = image;
- redraw();
- }
-}
-/**
- * Set the label's text.
- * The value <code>null</code> clears it.
- *
- * @param text the text to be displayed in the label or null
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setText(String text) {
- checkWidget();
- if (text == null) text = ""; //$NON-NLS-1$
- if (! text.equals(this.text)) {
- this.text = text;
- redraw();
- }
-}
-/**
- * Shorten the given text <code>t</code> so that its length doesn't exceed
- * the given width. The default implementation replaces characters in the
- * center of the original string with an ellipsis ("...").
- * Override if you need a different strategy.
- */
-protected String shortenText(GC gc, String t, int width) {
- if (t == null) return null;
- int w = gc.textExtent(ellipsis).x;
- int l = t.length();
- int pivot = l/2;
- int s = pivot;
- int e = pivot+1;
- while (s >= 0 && e < l) {
- String s1 = t.substring(0, s);
- String s2 = t.substring(e, l);
- int l1 = gc.textExtent(s1).x;
- int l2 = gc.textExtent(s2).x;
- if (l1+w+l2 < width) {
- t = s1 + ellipsis + s2;
- break;
- }
- s--;
- e++;
- }
- return t;
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.accessibility.*;
+
+/**
+ * A Label which supports aligned text and/or an image and different border styles.
+ * <p>
+ * If there is not enough space a CLabel uses the following strategy to fit the
+ * information into the available space:
+ * <pre>
+ * ignores the indent in left align mode
+ * ignores the image and the gap
+ * shortens the text by replacing the center portion of the label with an ellipsis
+ * shortens the text by removing the center portion of the label
+ * </pre>
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b>
+ * <dd>LEFT, RIGHT, CENTER, SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
+ * <dt><b>Events:</b>
+ * <dd></dd>
+ * </dl>
+ */
+public class CLabel extends Canvas {
+
+ /** Gap between icon and text */
+ private static final int GAP = 5;
+ /** Left and right margins */
+ private static final int INDENT = 3;
+ /** a string inserted in the middle of text that has been shortened */
+ private static final String ellipsis = "..."; //$NON-NLS-1$
+ /** the alignment. Either CENTER, RIGHT, LEFT. Default is LEFT*/
+ private int align = SWT.LEFT;
+ private int hIndent = INDENT;
+ private int vIndent = INDENT;
+ /** the current text */
+ private String text;
+ /** the current icon */
+ private Image image;
+ // The tooltip is used for two purposes - the application can set
+ // a tooltip or the tooltip can be used to display the full text when the
+ // the text has been truncated due to the label being too short.
+ // The appToolTip stores the tooltip set by the application. Control.tooltiptext
+ // contains whatever tooltip is currently being displayed.
+ private String appToolTipText;
+
+ private Image backgroundImage;
+ private Color[] gradientColors;
+ private int[] gradientPercents;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#LEFT
+ * @see SWT#RIGHT
+ * @see SWT#CENTER
+ * @see SWT#SHADOW_IN
+ * @see SWT#SHADOW_OUT
+ * @see SWT#SHADOW_NONE
+ * @see #getStyle
+ */
+public CLabel(Composite parent, int style) {
+ super(parent, checkStyle(style));
+
+ if ((style & SWT.CENTER) != 0) align = SWT.CENTER;
+ if ((style & SWT.RIGHT) != 0) align = SWT.RIGHT;
+ if ((style & SWT.LEFT) != 0) align = SWT.LEFT;
+
+ addPaintListener(new PaintListener(){
+ public void paintControl(PaintEvent event) {
+ onPaint(event);
+ }
+ });
+
+ addDisposeListener(new DisposeListener(){
+ public void widgetDisposed(DisposeEvent event) {
+ onDispose(event);
+ }
+ });
+
+ initAccessible();
+
+}
+/**
+ * Check the style bits to ensure that no invalid styles are applied.
+ */
+private static int checkStyle (int style) {
+ int mask = SWT.SHADOW_IN | SWT.SHADOW_OUT | SWT.SHADOW_NONE;
+ style = style & mask;
+ style |= SWT.NO_FOCUS;
+ //TEMPORARY CODE
+ /*
+ * The default background on carbon and some GTK themes is not a solid color
+ * but a texture. To show the correct default background, we must allow
+ * the operating system to draw it and therefore, we can not use the
+ * NO_BACKGROUND style. The NO_BACKGROUND style is not required on platforms
+ * that use double buffering which is true in both of these cases.
+ */
+ String platform = SWT.getPlatform();
+ if ("carbon".equals(platform) || "gtk".equals(platform)) return style; //$NON-NLS-1$ //$NON-NLS-2$
+ return style | SWT.NO_BACKGROUND;
+}
+public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ Point e = getTotalSize(image, text);
+ if (wHint == SWT.DEFAULT){
+ e.x += 2*hIndent;
+ } else {
+ e.x = wHint;
+ }
+ if (hHint == SWT.DEFAULT) {
+ e.y += 2*vIndent;
+ } else {
+ e.y = hHint;
+ }
+ return e;
+}
+/**
+ * Draw a rectangle in the given colors.
+ */
+private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
+ gc.setForeground(bottomright);
+ gc.drawLine(x+w, y, x+w, y+h);
+ gc.drawLine(x, y+h, x+w, y+h);
+
+ gc.setForeground(topleft);
+ gc.drawLine(x, y, x+w-1, y);
+ gc.drawLine(x, y, x, y+h-1);
+}
+/**
+ * Returns the alignment.
+ * The alignment style (LEFT, CENTER or RIGHT) is returned.
+ *
+ * @return SWT.LEFT, SWT.RIGHT or SWT.CENTER
+ */
+public int getAlignment() {
+ //checkWidget();
+ return align;
+}
+/**
+ * Return the CLabel's image or <code>null</code>.
+ *
+ * @return the image of the label or null
+ */
+public Image getImage() {
+ //checkWidget();
+ return image;
+}
+/**
+ * Compute the minimum size.
+ */
+private Point getTotalSize(Image image, String text) {
+ Point size = new Point(0, 0);
+
+ if (image != null) {
+ Rectangle r = image.getBounds();
+ size.x += r.width;
+ size.y += r.height;
+ }
+
+ GC gc = new GC(this);
+ if (text != null && text.length() > 0) {
+ Point e = gc.textExtent(text);
+ size.x += e.x;
+ size.y = Math.max(size.y, e.y);
+ if (image != null) size.x += GAP;
+ } else {
+ size.y = Math.max(size.y, gc.getFontMetrics().getHeight());
+ }
+ gc.dispose();
+
+ return size;
+}
+public void setToolTipText (String string) {
+ super.setToolTipText (string);
+ appToolTipText = super.getToolTipText();
+}
+/**
+ * Return the Label's text.
+ *
+ * @return the text of the label or null
+ */
+public String getText() {
+ //checkWidget();
+ return text;
+}
+public String getToolTipText () {
+ checkWidget();
+ return appToolTipText;
+}
+/**
+ * Paint the Label's border.
+ */
+private void paintBorder(GC gc, Rectangle r) {
+ Display disp= getDisplay();
+
+ Color c1 = null;
+ Color c2 = null;
+
+ int style = getStyle();
+ if ((style & SWT.SHADOW_IN) != 0) {
+ c1 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+ c2 = disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+ }
+ if ((style & SWT.SHADOW_OUT) != 0) {
+ c1 = disp.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
+ c2 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+ }
+
+ if (c1 != null && c2 != null) {
+ gc.setLineWidth(1);
+ drawBevelRect(gc, r.x, r.y, r.width-1, r.height-1, c1, c2);
+ }
+}
+private void initAccessible() {
+ Accessible accessible = getAccessible();
+ accessible.addAccessibleListener(new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ e.result = getText();
+ }
+
+ public void getHelp(AccessibleEvent e) {
+ e.result = getToolTipText();
+ }
+ });
+
+ accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
+ public void getChildAtPoint(AccessibleControlEvent e) {
+ Point pt = toControl(new Point(e.x, e.y));
+ e.childID = (getBounds().contains(pt)) ? ACC.CHILDID_SELF : ACC.CHILDID_NONE;
+ }
+
+ public void getLocation(AccessibleControlEvent e) {
+ Rectangle location = getBounds();
+ Point pt = toDisplay(new Point(location.x, location.y));
+ e.x = pt.x;
+ e.y = pt.y;
+ e.width = location.width;
+ e.height = location.height;
+ }
+
+ public void getChildCount(AccessibleControlEvent e) {
+ e.detail = 0;
+ }
+
+ public void getRole(AccessibleControlEvent e) {
+ e.detail = ACC.ROLE_LABEL;
+ }
+
+ public void getState(AccessibleControlEvent e) {
+ e.detail = ACC.STATE_READONLY;
+ }
+ });
+}
+private void onDispose(DisposeEvent event) {
+ gradientColors = null;
+ gradientPercents = null;
+ backgroundImage = null;
+ text = null;
+ image = null;
+ appToolTipText = null;
+}
+/*
+ * Process the paint event
+ */
+private void onPaint(PaintEvent event) {
+ Rectangle rect = getClientArea();
+ if (rect.width == 0 || rect.height == 0) return;
+
+ boolean shortenText = false;
+ String t = text;
+ Image img = image;
+ int availableWidth = rect.width - 2*hIndent;
+ Point extent = getTotalSize(img, t);
+ if (extent.x > availableWidth) {
+ img = null;
+ extent = getTotalSize(img, t);
+ if (extent.x > availableWidth) {
+ shortenText = true;
+ }
+ }
+
+ GC gc = event.gc;
+
+ // shorten the text
+ if (shortenText) {
+ t = shortenText(gc, text, availableWidth);
+ extent = getTotalSize(img, t);
+ if (appToolTipText == null) {
+ super.setToolTipText(text);
+ }
+ } else {
+ super.setToolTipText(appToolTipText);
+ }
+
+ // determine horizontal position
+ int x = rect.x + hIndent;
+ if (align == SWT.CENTER) {
+ x = (rect.width-extent.x)/2;
+ }
+ if (align == SWT.RIGHT) {
+ x = rect.width-extent.x - hIndent;
+ }
+
+ // draw a background image behind the text
+ try {
+ if (backgroundImage != null) {
+ // draw a background image behind the text
+ Rectangle imageRect = backgroundImage.getBounds();
+ gc.drawImage(backgroundImage, 0, 0, imageRect.width, imageRect.height,
+ 0, 0, rect.width, rect.height);
+ } else if (gradientColors != null) {
+ // draw a gradient behind the text
+ final Color oldBackground = gc.getBackground();
+ if (gradientColors.length == 1) {
+ if (gradientColors[0] != null) gc.setBackground(gradientColors[0]);
+ gc.fillRectangle(0, 0, rect.width, rect.height);
+ } else {
+ final Color oldForeground = gc.getForeground();
+ Color lastColor = gradientColors[0];
+ if (lastColor == null) lastColor = oldBackground;
+ for (int i = 0, pos = 0; i < gradientPercents.length; ++i) {
+ gc.setForeground(lastColor);
+ lastColor = gradientColors[i + 1];
+ if (lastColor == null) lastColor = oldBackground;
+ gc.setBackground(lastColor);
+ final int gradientWidth = (gradientPercents[i] * rect.width / 100) - pos;
+ gc.fillGradientRectangle(pos, 0, gradientWidth, rect.height, false);
+ pos += gradientWidth;
+ }
+ gc.setForeground(oldForeground);
+ }
+ gc.setBackground(oldBackground);
+ } else {
+ if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(rect);
+ }
+ }
+ } catch (SWTException e) {
+ if ((getStyle() & SWT.NO_BACKGROUND) != 0) {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(rect);
+ }
+ }
+
+ // draw border
+ int style = getStyle();
+ if ((style & SWT.SHADOW_IN) != 0 || (style & SWT.SHADOW_OUT) != 0) {
+ paintBorder(gc, rect);
+ }
+ // draw the image
+ if (img != null) {
+ Rectangle imageRect = img.getBounds();
+ gc.drawImage(img, 0, 0, imageRect.width, imageRect.height,
+ x, (rect.height-imageRect.height)/2, imageRect.width, imageRect.height);
+ x += imageRect.width + GAP;
+ }
+ // draw the text
+ if (t != null) {
+ int textHeight = gc.getFontMetrics().getHeight();
+ gc.setForeground(getForeground());
+ gc.drawText(t, x, rect.y + (rect.height-textHeight)/2, true);
+ }
+}
+/**
+ * Set the alignment of the CLabel.
+ * Use the values LEFT, CENTER and RIGHT to align image and text within the available space.
+ *
+ * @param align the alignment style of LEFT, RIGHT or CENTER
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the value of align is not one of SWT.LEFT, SWT.RIGHT or SWT.CENTER</li>
+ * </ul>
+ */
+public void setAlignment(int align) {
+ checkWidget();
+ if (align != SWT.LEFT && align != SWT.RIGHT && align != SWT.CENTER) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (this.align != align) {
+ this.align = align;
+ redraw();
+ }
+}
+
+public void setBackground (Color color) {
+ super.setBackground (color);
+ // Are these settings the same as before?
+ if (color != null && backgroundImage == null &&
+ gradientColors == null && gradientPercents == null) {
+ Color background = getBackground();
+ if (color.equals(background)) {
+ return;
+ }
+ }
+ backgroundImage = null;
+ gradientColors = null;
+ gradientPercents = null;
+ redraw ();
+}
+
+/**
+ * Specify a gradient of colours to be drawn in the background of the CLabel.
+ * <p>For example, to draw a gradient that varies from dark blue to blue and then to
+ * white and stays white for the right hald of the label, use the following call
+ * to setBackground:</p>
+ * <pre>
+ * clabel.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
+ * display.getSystemColor(SWT.COLOR_BLUE),
+ * display.getSystemColor(SWT.COLOR_WHITE),
+ * display.getSystemColor(SWT.COLOR_WHITE)},
+ * new int[] {25, 50, 100});
+ * </pre>
+ *
+ * @param colors an array of Color that specifies the colors to appear in the gradient
+ * in order of appearance from left to right; The value <code>null</code>
+ * clears the background gradient; the value <code>null</code> can be used
+ * inside the array of Color to specify the background color.
+ * @param percents an array of integers between 0 and 100 specifying the percent of the width
+ * of the widget at which the color should change; the size of the percents
+ * array must be one less than the size of the colors array.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the values of colors and percents are not consistant</li>
+ * </ul>
+ */
+public void setBackground(Color[] colors, int[] percents) {
+ checkWidget();
+ if (colors != null) {
+ if (percents == null || percents.length != colors.length - 1) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (getDisplay().getDepth() < 15) {
+ // Don't use gradients on low color displays
+ colors = new Color[] { colors[0] };
+ percents = new int[] { };
+ }
+ for (int i = 0; i < percents.length; i++) {
+ if (percents[i] < 0 || percents[i] > 100) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (i > 0 && percents[i] < percents[i-1]) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ }
+ }
+
+ // Are these settings the same as before?
+ final Color background = getBackground();
+ if (backgroundImage == null) {
+ if ((gradientColors != null) && (colors != null) &&
+ (gradientColors.length == colors.length)) {
+ boolean same = false;
+ for (int i = 0; i < gradientColors.length; i++) {
+ same = (gradientColors[i] == colors[i]) ||
+ ((gradientColors[i] == null) && (colors[i] == background)) ||
+ ((gradientColors[i] == background) && (colors[i] == null));
+ if (!same) break;
+ }
+ if (same) {
+ for (int i = 0; i < gradientPercents.length; i++) {
+ same = gradientPercents[i] == percents[i];
+ if (!same) break;
+ }
+ }
+ if (same) return;
+ }
+ } else {
+ backgroundImage = null;
+ }
+ // Store the new settings
+ if (colors == null) {
+ gradientColors = null;
+ gradientPercents = null;
+ } else {
+ gradientColors = new Color[colors.length];
+ for (int i = 0; i < colors.length; ++i)
+ gradientColors[i] = (colors[i] != null) ? colors[i] : background;
+ gradientPercents = new int[percents.length];
+ for (int i = 0; i < percents.length; ++i)
+ gradientPercents[i] = percents[i];
+ }
+ // Refresh with the new settings
+ redraw();
+}
+/**
+ * Set the image to be drawn in the background of the label.
+ *
+ * @param image the image to be drawn in the background
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBackground(Image image) {
+ checkWidget();
+ if (image == backgroundImage) return;
+ if (image != null) {
+ gradientColors = null;
+ gradientPercents = null;
+ }
+ backgroundImage = image;
+ redraw();
+
+}
+public void setFont(Font font) {
+ super.setFont(font);
+ redraw();
+}
+/**
+ * Set the label's Image.
+ * The value <code>null</code> clears it.
+ *
+ * @param image the image to be displayed in the label or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage(Image image) {
+ checkWidget();
+ if (image != this.image) {
+ this.image = image;
+ redraw();
+ }
+}
+/**
+ * Set the label's text.
+ * The value <code>null</code> clears it.
+ *
+ * @param text the text to be displayed in the label or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText(String text) {
+ checkWidget();
+ if (text == null) text = ""; //$NON-NLS-1$
+ if (! text.equals(this.text)) {
+ this.text = text;
+ redraw();
+ }
+}
+/**
+ * Shorten the given text <code>t</code> so that its length doesn't exceed
+ * the given width. The default implementation replaces characters in the
+ * center of the original string with an ellipsis ("...").
+ * Override if you need a different strategy.
+ */
+protected String shortenText(GC gc, String t, int width) {
+ if (t == null) return null;
+ int w = gc.textExtent(ellipsis).x;
+ int l = t.length();
+ int pivot = l/2;
+ int s = pivot;
+ int e = pivot+1;
+ while (s >= 0 && e < l) {
+ String s1 = t.substring(0, s);
+ String s2 = t.substring(e, l);
+ int l1 = gc.textExtent(s1).x;
+ int l2 = gc.textExtent(s2).x;
+ if (l1+w+l2 < width) {
+ t = s1 + ellipsis + s2;
+ break;
+ }
+ s--;
+ e++;
+ }
+ return t;
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
index 7406e5a0f1..029413724f 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java
@@ -1,2102 +1,2102 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.accessibility.*;
-
-/**
- * Instances of this class implement the notebook user interface
- * metaphor. It allows the user to select a notebook page from
- * set of pages.
- * <p>
- * The item children that may be added to instances of this class
- * must be of type <code>CTabItem</code>.
- * <code>Control</code> children are created and then set into a
- * tab item using <code>CTabItem#setControl</code>.
- * </p><p>
- * Note that although this class is a subclass of <code>Composite</code>,
- * it does not make sense to set a layout on it.
- * </p><p>
- * <dl>
- * <dt><b>Styles:</b></dt>
- * <dd>TOP, BOTTOM, FLAT</dd>
- * <dt><b>Events:</b></dt>
- * <dd>Selection</dd>
- * <dd>"CTabFolder"</dd>
- * </dl>
- * <p>
- * Note: Only one of the styles TOP and BOTTOM
- * may be specified.
- * </p><p>
- * IMPORTANT: This class is <em>not</em> intended to be subclassed.
- * </p>
- */
-
-public class CTabFolder extends Composite {
-
- /**
- * marginWidth specifies the number of pixels of horizontal margin
- * that will be placed along the left and right edges of the form.
- *
- * The default value is 0.
- */
- public int marginWidth = 0;
- /**
- * marginHeight specifies the number of pixels of vertical margin
- * that will be placed along the top and bottom edges of the form.
- *
- * The default value is 0.
- */
- public int marginHeight = 0;
-
- /**
- * Color of innermost line of drop shadow border.
- */
- public static RGB borderInsideRGB = new RGB (132, 130, 132);
- /**
- * Color of middle line of drop shadow border.
- */
- public static RGB borderMiddleRGB = new RGB (143, 141, 138);
- /**
- * Color of outermost line of drop shadow border.
- */
- public static RGB borderOutsideRGB = new RGB (171, 168, 165);
-
- /*
- * A multiple of the tab height that specifies the minimum width to which a tab
- * will be compressed before scrolling arrows are used to navigate the tabs.
- */
- public int MIN_TAB_WIDTH = 3;
-
- /* sizing, positioning */
- int xClient, yClient;
- boolean onBottom = false;
- boolean fixedTabHeight;
- int tabHeight;
-
- /* item management */
- private CTabItem items[] = new CTabItem[0];
- private int selectedIndex = -1;
- int topTabIndex = -1; // index of the left most visible tab.
-
- /* External Listener management */
- private CTabFolderListener[] tabListeners = new CTabFolderListener[0];
-
- /* Color appearance */
- Image backgroundImage;
- Color[] gradientColors;
- int[] gradientPercents;
- Color selectionForeground;
- Color background;
-
- // internal constants
- private static final int DEFAULT_WIDTH = 64;
- private static final int DEFAULT_HEIGHT = 64;
-
- // scrolling arrows
- private ToolBar arrowBar;
- private Image arrowLeftImage;
- private Image arrowRightImage;
-
- private Control topRight;
-
- // close button
- boolean showClose = false;
- private Image closeImage;
- ToolBar closeBar;
- private ToolBar inactiveCloseBar;
- private CTabItem inactiveItem;
-
- // borders
- boolean showBorders = false;
- private int borderBottom = 0;
- private int borderLeft = 0;
- private int borderRight = 0;
- private int borderTop = 0;
- private Color borderColor1;
- private Color borderColor2;
- private Color borderColor3;
-
- // when disposing CTabFolder, don't try to layout the items or
- // change the selection as each child is destroyed.
- private boolean inDispose = false;
-
- // keep track of size changes in order to redraw only affected area
- // on Resize
- private Point oldSize;
- private Font oldFont;
-
- // insertion marker
- int insertionIndex = -2; // Index of insert marker. Marker always shown after index.
- // -2 means no insert marker
-
- // tool tip
- private Shell tip;
- private Label label;
- private boolean showToolTip = false;
- private CTabItem toolTipItem;
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#TOP
- * @see SWT#BOTTOM
- * @see SWT#FLAT
- * @see #getStyle
- */
-public CTabFolder(Composite parent, int style) {
- super(parent, checkStyle (style));
-
- onBottom = (getStyle() & SWT.BOTTOM) != 0;
-
- borderColor1 = new Color(getDisplay(), borderInsideRGB);
- borderColor2 = new Color(getDisplay(), borderMiddleRGB);
- borderColor3 = new Color(getDisplay(), borderOutsideRGB);
-
- // tool tip support
- tip = new Shell (getShell(), SWT.ON_TOP);
- label = new Label (tip, SWT.CENTER);
-
- // Add all listeners
- Listener listener = new Listener() {
- public void handleEvent(Event event) {
- switch (event.type) {
- case SWT.Dispose: onDispose(); break;
- case SWT.Paint: onPaint(event); break;
- case SWT.Resize: onResize(); break;
- case SWT.MouseDoubleClick: onMouseDoubleClick(event); break;
- case SWT.MouseDown: onMouseDown(event); break;
- case SWT.MouseExit: onMouseExit(event); break;
- case SWT.MouseHover: onMouseHover(event); break;
- case SWT.MouseMove: onMouseMove(event); break;
- case SWT.FocusIn: onFocus(event); break;
- case SWT.FocusOut: onFocus(event); break;
- case SWT.KeyDown: onKeyDown(event); break;
- case SWT.Traverse: onTraverse(event); break;
- }
- }
- };
-
- int[] folderEvents = new int[]{
- SWT.Dispose,
- SWT.Paint,
- SWT.Resize,
- SWT.MouseDoubleClick,
- SWT.MouseDown,
- SWT.MouseExit,
- SWT.MouseHover,
- SWT.MouseMove,
- SWT.FocusIn,
- SWT.FocusOut,
- SWT.KeyDown,
- SWT.Traverse,
- };
- for (int i = 0; i < folderEvents.length; i++) {
- addListener(folderEvents[i], listener);
- }
-
- createArrowBar();
- createCloseBar();
-
- setBorderVisible((style & SWT.BORDER) != 0);
-
- initAccessible();
-
-}
-private static int checkStyle (int style) {
- int mask = SWT.TOP | SWT.BOTTOM | SWT.FLAT;
- style = style & mask;
- // TOP and BOTTOM are mutually exlusive.
- // TOP is the default
- if ((style & SWT.TOP) != 0)
- style = style & ~(SWT.TOP | SWT.BOTTOM) | SWT.TOP;
- // reduce the flash by not redrawing the entire area on a Resize event
- style |= SWT.NO_REDRAW_RESIZE;
- return style;
-}
-/**
-* Adds the listener to receive events.
-* <p>
-*
-* @param listener the listener
-*
-* @exception SWTError <ul>
-* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
-* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
-* <li>ERROR_NULL_ARGUMENT when listener is null</li>
-* </ul>
-*/
-public void addSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- TypedListener typedListener = new TypedListener(listener);
- addListener(SWT.Selection, typedListener);
- addListener(SWT.DefaultSelection, typedListener);
-}
-/**
- * Adds the listener to the collection of listeners who will
- * be notified when a tab item is closed.
- *
- * @param listener the listener which should be notified
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
- * </ul>
- *
- * @see CTabFolderListener
- * @see #removeCTabFolderListener
- */
-public void addCTabFolderListener(CTabFolderListener listener) {
- checkWidget();
- if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- // add to array
- CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
- System.arraycopy(tabListeners, 0, newTabListeners, 0, tabListeners.length);
- tabListeners = newTabListeners;
- tabListeners[tabListeners.length - 1] = listener;
- showClose = true;
- setButtonBounds();
-}
-private void closeNotify(CTabItem item, int time) {
- if (item == null) return;
-
- CTabFolderEvent event = new CTabFolderEvent(this);
- event.widget = this;
- event.time = time;
- event.item = item;
- event.doit = true;
- if (tabListeners != null) {
- for (int i = 0; i < tabListeners.length; i++) {
- tabListeners[i].itemClosed(event);
- }
- }
- if (event.doit) {
- item.dispose();
- }
-}
-public Point computeSize (int wHint, int hHint, boolean changed) {
- checkWidget();
- int minWidth = 0;
- int minHeight = 0;
-
- // preferred width of tab area to show all tabs
- GC gc = new GC(this);
- for (int i = 0; i < items.length; i++) {
- minWidth += items[i].preferredWidth(gc);
- }
- gc.dispose();
-
- // preferred size of controls in tab items
- for (int i = 0; i < items.length; i++) {
- Control control = items[i].getControl();
- if (control != null && !control.isDisposed()){
- Point size = control.computeSize (wHint, hHint);
- minWidth = Math.max (minWidth, size.x);
- minHeight = Math.max (minHeight, size.y);
- }
- }
- if (minWidth == 0) minWidth = DEFAULT_WIDTH;
- if (minHeight == 0) minHeight = DEFAULT_HEIGHT;
-
- if (wHint != SWT.DEFAULT) minWidth = wHint;
- if (hHint != SWT.DEFAULT) minHeight = hHint;
-
- Rectangle trim = computeTrim(0, 0, minWidth, minHeight);
- return new Point (trim.width, trim.height);
-}
-public Rectangle computeTrim (int x, int y, int width, int height) {
- checkWidget();
- if (items.length == 0) {
- if (!showBorders) return new Rectangle(x, y, width, height);
- int trimX = x - borderRight - 1;
- int trimY = y - borderBottom - 1;
- int trimWidth = width + borderRight + 2;
- int trimHeight = height + borderBottom + 2;
- return new Rectangle (trimX, trimY, trimWidth, trimHeight);
- } else {
- int trimX = x - marginWidth - borderLeft;
- int trimY = y - marginHeight - tabHeight - borderTop - 1;
- // -1 is for the line at the bottom of the tabs
- if (onBottom) {
- trimY = y - marginHeight - borderTop;
- }
- int trimWidth = width + borderLeft + borderRight + 2*marginWidth;
- int trimHeight = height + borderTop + borderBottom + 2*marginHeight + tabHeight + 1;
- return new Rectangle (trimX, trimY, trimWidth, trimHeight);
- }
-}
-/**
- * Create the specified item at 'index'.
- */
-void createItem (CTabItem item, int index) {
- if (0 > index || index > getItemCount ()){
- SWT.error (SWT.ERROR_INVALID_RANGE);
- }
- // grow by one and rearrange the array.
- CTabItem[] newItems = new CTabItem [items.length + 1];
- System.arraycopy(items, 0, newItems, 0, index);
- newItems[index] = item;
- System.arraycopy(items, index, newItems, index + 1, items.length - index);
- items = newItems;
-
- item.parent = this;
-
- if (selectedIndex >= index) {
- selectedIndex ++;
- }
- if (items.length == 1) {
- topTabIndex = 0;
- resetTabSize(true);
- } else {
- setItemBounds();
- showItem(item);
- }
-
- if (items.length == 1) {
- redraw();
- } else {
- redrawTabArea(-1);
- }
-}
-
-private void createArrowBar() {
- // create arrow buttons for scrolling
- arrowBar = new ToolBar(this, SWT.FLAT);
- arrowBar.setVisible(false);
- arrowBar.setBackground(background);
- ToolItem scrollLeft = new ToolItem(arrowBar, SWT.PUSH);
- scrollLeft.setEnabled(false);
- ToolItem scrollRight = new ToolItem(arrowBar, SWT.PUSH);
- scrollRight.setEnabled(false);
-
- scrollLeft.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- scroll_scrollLeft();
- }
- });
- scrollRight.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- scroll_scrollRight();
- }
- });
-
-}
-private void createCloseBar() {
- closeBar = new ToolBar(this, SWT.FLAT);
- closeBar.setVisible(false);
- if (gradientColors != null && gradientColors.length > 0) {
- closeBar.setBackground(gradientColors[gradientColors.length - 1]);
- } else {
- closeBar.setBackground(background);
- }
- ToolItem closeItem = new ToolItem(closeBar, SWT.PUSH);
-
- inactiveCloseBar = new ToolBar(this, SWT.FLAT);
- inactiveCloseBar.setVisible(false);
- inactiveCloseBar.setBackground(background);
- ToolItem inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH);
-
- closeItem.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- closeNotify(getSelection(), event.time);
- }
- });
- inactiveCloseItem.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- closeNotify(inactiveItem, event.time);
- inactiveCloseBar.setVisible(false);
- inactiveItem = null;
- }
- });
- inactiveCloseBar.addListener (SWT.MouseExit, new Listener() {
- public void handleEvent(Event event) {
- if (inactiveItem != null) {
- Rectangle itemBounds = inactiveItem.getBounds();
- if (itemBounds.contains(event.x, event.y)) return;
- }
- inactiveCloseBar.setVisible(false);
- inactiveItem = null;
- }
- });
-
-}
-/**
- * Destroy the specified item.
- */
-void destroyItem (CTabItem item) {
- if (inDispose) return;
-
- int index = indexOf(item);
- if (index == -1) return; // should this trigger an error?
-
- insertionIndex = -2;
-
- if (items.length == 1) {
- items = new CTabItem[0];
- selectedIndex = -1;
- topTabIndex = 0;
-
- Control control = item.getControl();
- if (control != null && !control.isDisposed()) {
- control.setVisible(false);
- }
- closeBar.setVisible(false);
- if (!fixedTabHeight) tabHeight = 0;
- redraw();
- return;
- }
-
- // shrink by one and rearrange the array.
- CTabItem[] newItems = new CTabItem [items.length - 1];
- System.arraycopy(items, 0, newItems, 0, index);
- System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
- items = newItems;
-
- if (topTabIndex == items.length) {
- --topTabIndex;
- }
-
- // move the selection if this item is selected
- if (selectedIndex == index) {
- Control control = item.getControl();
- if (control != null && !control.isDisposed()) {
- control.setVisible(false);
- }
- selectedIndex = -1;
- setSelection(Math.max(0, index - 1), true);
- } else if (selectedIndex > index) {
- selectedIndex --;
- }
-
- setItemBounds();
- redrawTabArea(-1);
-}
-private void onKeyDown(Event e) {
- if (e.keyCode == SWT.ARROW_LEFT) {
- if (selectedIndex > 0) {
- setSelection(selectedIndex - 1, true);
- }
- }
- if (e.keyCode == SWT.ARROW_RIGHT) {
- if (selectedIndex < items.length - 1) {
- setSelection(selectedIndex + 1, true);
- }
- }
-}
-/**
- * Dispose the items of the receiver
- */
-private void onDispose() {
- /*
- * Usually when an item is disposed, destroyItem will change the size of the items array,
- * reset the bounds of all the tabs and manage the widget associated with the tab.
- * Since the whole folder is being disposed, this is not necessary. For speed
- * the inDispose flag is used to skip over this part of the item dispose.
- */
- inDispose = true;
-
- int length = items.length;
- for (int i = 0; i < length; i++) {
- if (items[i] != null) {
- items[i].dispose();
- }
- }
-
- // clean up resources
- if (tip != null && !tip.isDisposed()) {
- tip.dispose();
- tip = null;
- label = null;
- }
-
- if (arrowLeftImage != null) arrowLeftImage.dispose();
- arrowLeftImage = null;
- if (arrowRightImage != null) arrowRightImage.dispose();
- arrowRightImage = null;
- if (closeImage != null) closeImage.dispose();
- closeImage = null;
-
- gradientColors = null;
- gradientPercents = null;
- backgroundImage = null;
-
- if (borderColor1 != null) borderColor1.dispose();
- borderColor1 = null;
-
- if (borderColor2 != null) borderColor2.dispose();
- borderColor2 = null;
-
- if (borderColor3 != null) borderColor3.dispose();
- borderColor3 = null;
-}
-private void onFocus(Event e) {
- checkWidget();
- if (selectedIndex >= 0) {
- redrawTabArea(selectedIndex);
- } else {
- setSelection(0, true);
- }
-}
-/**
- * Draw a border around the receiver.
- */
-private void drawBorder(GC gc) {
-
- Rectangle d = super.getClientArea();
-
- if (showBorders) {
- if ((getStyle() & SWT.FLAT) != 0) {
- gc.setForeground(borderColor1);
- gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height - 1);
- } else {
- gc.setForeground(borderColor1);
- gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height - 3);
-
- gc.setForeground(borderColor2);
- gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y + d.height - 2);
- gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y + d.height - 1);
-
- gc.setForeground(borderColor3);
- gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y + d.height - 1);
- gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y + d.height - 2);
-
- // fill in corners with parent's background
- gc.setForeground(getParent().getBackground());
- gc.drawLine(d.x + d.width - 2, d.y, d.x + d.width - 1, d.y);
- gc.drawLine(d.x + d.width - 1, d.y + 1, d.x + d.width - 1, d.y + 1);
-
- gc.drawLine(d.x, d.y + d.height - 2, d.x, d.y + d.height - 2);
- gc.drawLine(d.x, d.y + d.height - 1, d.x + 1, d.y + d.height - 1);
-
- gc.drawLine(d.x + d.width - 1, d.y + d.height - 1, d.x + d.width - 1, d.y + d.height - 1);
- }
-
- }
-
- // draw a separator line
- if (items.length > 0) {
- int lineY = d.y + borderTop + tabHeight;
- if (onBottom) {
- lineY = d.y + d.height - borderBottom - tabHeight - 1;
- }
- gc.setForeground(borderColor1);
- gc.drawLine(d.x + borderLeft, lineY, d.x + d.width - borderRight, lineY);
- }
-
- gc.setForeground(getForeground());
-}
-public Rectangle getClientArea() {
- checkWidget();
- Point size = getSize();
- if (items.length == 0) {
- if (!showBorders) return super.getClientArea();
- int width = size.x - borderRight - 2;
- int height = size.y - borderBottom - 2;
- return new Rectangle(borderRight + 1, borderBottom + 1, width, height);
- } else {
- int width = size.x - 2*marginWidth - borderLeft - borderRight;
- int height = size.y - 2*marginHeight - borderTop - borderBottom - tabHeight - 1;
- return new Rectangle(xClient, yClient, width, height);
- }
-}
-/**
- * Returns the height of the tab
- *
- * @return the height of the tab
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * </ul>
- */
-public int getTabHeight(){
- checkWidget();
- return tabHeight;
-}
-
-/**
- * Return the tab that is located at the specified index.
- *
- * @return the item at the specified index
- */
-public CTabItem getItem (int index) {
- //checkWidget();
- if (index < 0 || index >= items.length)
- SWT.error(SWT.ERROR_INVALID_RANGE);
- return items [index];
-}
-/**
-* Gets the item at a point in the widget.
-* <p>
-*
-* @return the item at a point
-*/
-public CTabItem getItem (Point pt) {
- //checkWidget();
- if (items.length == 0) return null;
- int lastItem = getLastItem();
- lastItem = Math.min(items.length - 1, lastItem + 1);
- for (int i = topTabIndex; i <= lastItem; i++) {
- Rectangle bounds = items[i].getBounds();
- if (bounds.contains(pt)) return items[i];
- }
- return null;
-}
-/**
- * Return the number of tabs in the folder.
- *
- * @return the number of tabs in the folder
- */
-public int getItemCount(){
- //checkWidget();
- return items.length;
-}
-/**
- * Return the tab items.
- *
- * @return the tab items
- */
-public CTabItem [] getItems() {
- //checkWidget();
- CTabItem[] tabItems = new CTabItem [items.length];
- System.arraycopy(items, 0, tabItems, 0, items.length);
- return tabItems;
-}
-
-private int getLastItem(){
- if (items.length == 0) return -1;
- Rectangle area = getClientArea();
- if (area.width <= 0) return 0;
- Rectangle toolspace = getToolSpace();
- if (toolspace.width == 0) return items.length -1;
- int width = area.width - toolspace.width;
- int index = topTabIndex;
- int tabWidth = items[index].width;
- while (index < items.length - 1) {
- tabWidth += items[index + 1].width;
- if (tabWidth > width) break;
- index++;
- }
- return index;
-}
-/**
- * Return the selected tab item, or an empty array if there
- * is no selection.
- *
- * @return the selected tab item
- */
-public CTabItem getSelection() {
- //checkWidget();
- if (selectedIndex == -1) return null;
- return items[selectedIndex];
-}
-/**
- * Return the index of the selected tab item, or -1 if there
- * is no selection.
- *
- * @return the index of the selected tab item or -1
- */
-public int getSelectionIndex() {
- //checkWidget();
- return selectedIndex;
-}
-private Rectangle getToolSpace() {
- boolean showArrows = scroll_leftVisible() || scroll_rightVisible();
- if (!showArrows && topRight == null) return new Rectangle(0, 0, 0, 0);
- Rectangle toolspace;
- if (showArrows) {
- toolspace = arrowBar.getBounds();
- toolspace.width += borderRight;
- if (topRight != null) toolspace.width += topRight.getSize().x;
- } else {
- toolspace = topRight.getBounds();
- toolspace.width += borderRight;
- }
- return toolspace;
-}
-/**
- * Returns the control in the top right corner of the tab folder.
- * Typically this is a close button or a composite with a menu and close button.
- *
- * @since 2.1
- *
- * @return the control in the top right corner of the tab folder or null
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * </ul>
- */
-public Control getTopRight() {
- checkWidget();
- return topRight;
-}
-
-/**
- * Return the index of the specified tab or -1 if the tab is not
- * in the receiver.
- *
- * @return the index of the specified tab item or -1
- *
- * @exception SWTError <ul>
- * <li>ERROR_NULL_ARGUMENT when the item is null</li>
- * </ul>
- */
-public int indexOf(CTabItem item) {
- //checkWidget();
- if (item == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- for (int i = 0; i < items.length; i++) {
- if (items[i] == item) return i;
- }
- return -1;
-}
-
-private void initAccessible() {
- final Accessible accessible = getAccessible();
- accessible.addAccessibleListener(new AccessibleAdapter() {
- public void getName(AccessibleEvent e) {
- String name = null;
- int childID = e.childID;
- if (childID >= 0 && childID < items.length) {
- name = items[childID].getText();
- int index = name.indexOf('&');
- if (index > 0) {
- name = name.substring(0, index) + name.substring(index + 1);
- }
- }
- e.result = name;
- }
-
- public void getHelp(AccessibleEvent e) {
- String help = null;
- int childID = e.childID;
- if (childID == ACC.CHILDID_SELF) {
- help = getToolTipText();
- } else if (childID >= 0 && childID < items.length) {
- help = items[childID].getToolTipText();
- }
- e.result = help;
- }
-
- public void getKeyboardShortcut(AccessibleEvent e) {
- String shortcut = null;
- int childID = e.childID;
- if (childID >= 0 && childID < items.length) {
- String text = items[childID].getText();
- if (text != null) {
- char mnemonic = getMnemonic(text);
- if (mnemonic != '\0') {
- shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
- }
- }
- }
- e.result = shortcut;
- }
- });
-
- accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
- public void getChildAtPoint(AccessibleControlEvent e) {
- Point testPoint = toControl(new Point(e.x, e.y));
- int childID = ACC.CHILDID_NONE;
- for (int i = 0; i < items.length; i++) {
- if (items[i].getBounds().contains(testPoint)) {
- childID = i;
- break;
- }
- }
- if (childID == ACC.CHILDID_NONE) {
- Rectangle location = getBounds();
- location.height = location.height - getClientArea().height;
- if (location.contains(testPoint)) {
- childID = ACC.CHILDID_SELF;
- }
- }
- e.childID = childID;
- }
-
-
- public void getLocation(AccessibleControlEvent e) {
- Rectangle location = null;
- int childID = e.childID;
- if (childID == ACC.CHILDID_SELF) {
- location = getBounds();
- }
- if (childID >= 0 && childID < items.length) {
- location = items[childID].getBounds();
- }
- if (location != null) {
- Point pt = toDisplay(new Point(location.x, location.y));
- e.x = pt.x;
- e.y = pt.y;
- e.width = location.width;
- e.height = location.height;
- }
- }
-
- public void getChildCount(AccessibleControlEvent e) {
- e.detail = items.length;
- }
-
- public void getDefaultAction(AccessibleControlEvent e) {
- String action = null;
- int childID = e.childID;
- if (childID >= 0 && childID < items.length) {
- action = "Switch"; //$NON-NLS-1$
- }
- e.result = action;
- }
-
- public void getFocus(AccessibleControlEvent e) {
- int childID = ACC.CHILDID_NONE;
- if (isFocusControl()) {
- if (selectedIndex == -1) {
- childID = ACC.CHILDID_SELF;
- } else {
- childID = selectedIndex;
- }
- }
- e.childID = childID;
- }
-
- public void getRole(AccessibleControlEvent e) {
- int role = 0;
- int childID = e.childID;
- if (childID == ACC.CHILDID_SELF) {
- role = ACC.ROLE_TABFOLDER;
- } else if (childID >= 0 && childID < items.length) {
- role = ACC.ROLE_TABITEM;
- }
- e.detail = role;
- }
-
- public void getSelection(AccessibleControlEvent e) {
- e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
- }
-
- public void getState(AccessibleControlEvent e) {
- int state = 0;
- int childID = e.childID;
- if (childID == ACC.CHILDID_SELF) {
- state = ACC.STATE_NORMAL;
- } else if (childID >= 0 && childID < items.length) {
- state = ACC.STATE_SELECTABLE;
- if (isFocusControl()) {
- state |= ACC.STATE_FOCUSABLE;
- }
- if (selectedIndex == childID) {
- state |= ACC.STATE_SELECTED;
- if (isFocusControl()) {
- state |= ACC.STATE_FOCUSED;
- }
- }
- }
- e.detail = state;
- }
-
- public void getChildren(AccessibleControlEvent e) {
- Object[] children = new Object[items.length];
- for (int i = 0; i < items.length; i++) {
- children[i] = new Integer(i);
- }
- e.children = children;
- }
- });
-
- addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- if (isFocusControl()) {
- if (selectedIndex == -1) {
- accessible.setFocus(ACC.CHILDID_SELF);
- } else {
- accessible.setFocus(selectedIndex);
- }
- }
- }
- });
-
- addListener(SWT.FocusIn, new Listener() {
- public void handleEvent(Event event) {
- if (selectedIndex == -1) {
- accessible.setFocus(ACC.CHILDID_SELF);
- } else {
- accessible.setFocus(selectedIndex);
- }
- }
- });
-}
-
-private void setButtonBounds() {
-
- updateArrowBar();
- updateCloseBar();
-
- Rectangle area = super.getClientArea();
-
- int offset = 0;
- if (topRight != null) {
- Point size = topRight.computeSize(SWT.DEFAULT, tabHeight);
- int x = area.x + area.width - borderRight - size.x;
- int y = onBottom ? area.y + area.height - borderBottom - size.y : area.y + borderTop;
- topRight.setBounds(x, y, size.x, size.y);
- offset = size.x;
- }
- boolean leftVisible = scroll_leftVisible();
- boolean rightVisible = scroll_rightVisible();
- if (leftVisible || rightVisible) {
- Point size = arrowBar.computeSize(SWT.DEFAULT, tabHeight);
- int x = area.x + area.width - borderRight - size.x - offset;
- int y = (onBottom) ? area.y + area.height - borderBottom - size.y : area.y + borderTop;
-
- arrowBar.setBounds(x, y, size.x, size.y);
- ToolItem[] items = arrowBar.getItems();
- items[0].setEnabled(leftVisible);
- items[1].setEnabled(rightVisible);
- arrowBar.setVisible(true);
- } else {
- arrowBar.setVisible(false);
- }
-
- // When the close button is right at the edge of the Tab folder, hide it because
- // otherwise it may block off a part of the border on the right
- if (showClose) {
- inactiveCloseBar.setVisible(false);
- CTabItem item = getSelection();
- if (item == null) {
- closeBar.setVisible(false);
- } else {
- int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
- Point size = closeBar.computeSize(SWT.DEFAULT, toolbarHeight);
- int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
- int y = item.y + Math.max(0, (item.height - toolbarHeight)/2);
- closeBar.setBounds(x, y, size.x, toolbarHeight);
- Rectangle toolspace = getToolSpace();
- Point folderSize = getSize();
- boolean visible = (toolspace.width == 0 || x < toolspace.x) && x + size.x < folderSize.x - borderRight;
- closeBar.setVisible(visible);
- }
- }
-}
-private boolean setItemLocation() {
- if (items.length == 0) return false;
- Rectangle area = super.getClientArea();
- int x = area.x;
- int y = area.y + borderTop;
- if (onBottom) y = Math.max(0, area.y + area.height - borderBottom - tabHeight);
-
- boolean changed = false;
- for (int i = topTabIndex - 1; i>=0; i--) {
- // if the first visible tab is not the first tab
- CTabItem tab = items[i];
- x -= tab.width;
- if (!changed && (tab.x != x || tab.y != y) ) changed = true;
- // layout tab items from right to left thus making them invisible
- tab.x = x;
- tab.y = y;
- }
-
- x = area.x + borderLeft;
- for (int i = topTabIndex; i < items.length; i++) {
- // continue laying out remaining, visible items left to right
- CTabItem tab = items[i];
- tab.x = x;
- tab.y = y;
- x = x + tab.width;
- }
- setButtonBounds();
- return changed;
-}
-private void setLastItem(int index) {
- if (index < 0 || index > items.length - 1) return;
- Rectangle area = getClientArea();
- if (area.width <= 0) return;
- int maxWidth = area.width;
- Rectangle toolspace = getToolSpace();
- if (toolspace.width > 0){
- maxWidth -= toolspace.width;
- }
- int tabWidth = items[index].width;
- while (index > 0) {
- tabWidth += items[index - 1].width;
- if (tabWidth > maxWidth) break;
- index--;
- }
- topTabIndex = index;
- setItemLocation();
- redrawTabArea(-1);
-}
-/**
- * Layout the items and store the client area size.
- */
-boolean setItemBounds() {
- boolean changed = false;
- if (isDisposed()) return changed;
- Rectangle area = super.getClientArea();
-
- xClient = area.x + borderLeft + marginWidth;
- if (onBottom) {
- yClient = area.y + borderTop + marginHeight;
- } else {
- yClient = area.y + borderTop + tabHeight + 1 + marginHeight;
- // +1 is for the line at the bottom of the tabs
- }
-
- if (area.width <= 0 || area.height <= 0 || items.length == 0) return changed;
-
- int[] widths = new int[items.length];
- GC gc = new GC(this);
- for (int i = 0; i < items.length; i++) {
- widths[i] = items[i].preferredWidth(gc);
- }
- gc.dispose();
-
- int oldAverageWidth = 0;
- int averageWidth = (area.width - borderLeft - borderRight) / items.length;
- while (averageWidth > oldAverageWidth) {
- int width = area.width - borderLeft - borderRight;
- int count = items.length;
- for (int i = 0; i < items.length; i++) {
- if (widths[i] < averageWidth) {
- width -= widths[i];
- count--;
- }
- }
- oldAverageWidth = averageWidth;
- if (count > 0) {
- averageWidth = width / count;
- }
- }
- averageWidth = Math.max(averageWidth, MIN_TAB_WIDTH * tabHeight);
- for (int i = 0; i < items.length; i++) {
- if (widths[i] > averageWidth) {
- widths[i] = averageWidth;
- }
- }
-
- int totalWidth = 0;
- for (int i = 0; i < items.length; i++) {
- CTabItem tab = items[i];
- if (tab.height != tabHeight || tab.width != widths[i]) changed = true;
- tab.height = tabHeight;
- tab.width = widths[i];
- totalWidth += widths[i];
- }
-
- int areaWidth = area.x + area.width - borderRight;
- if (totalWidth <= areaWidth) {
- topTabIndex = 0;
- }
- if (setItemLocation()) changed = true;
-
- // Is there a gap after last item showing
- if (correctLastItem()) changed = true;
- return changed;
-}
-private boolean onMnemonic (Event event) {
- char key = event.character;
- for (int i = 0; i < items.length; i++) {
- if (items[i] != null) {
- char mnemonic = getMnemonic (items[i].getText ());
- if (mnemonic != '\0') {
- if (Character.toUpperCase (key) == Character.toUpperCase (mnemonic)) {
- setSelection(i, true);
- return true;
- }
- }
- }
- }
- return false;
-}
-/**
- * Paint the receiver.
- */
-private void onPaint(Event event) {
- Font font = getFont();
- if (oldFont == null || !oldFont.equals(font)) {
- oldFont = font;
- resetTabSize(true);
- }
- GC gc = event.gc;
- Rectangle rect = super.getClientArea();
- if (items.length == 0) {
- if (showBorders) {
- if ((getStyle() & SWT.FLAT) != 0) {
- gc.setForeground(borderColor1);
- gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 1, rect.y + rect.height - 1);
- } else {
- gc.setForeground(borderColor1);
- gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 3, rect.y + rect.height - 3);
-
- // fill in right and bottom edges with parent's background
- gc.setBackground(getParent().getBackground());
- gc.fillRectangle(rect.x + rect.width - 2, rect.y, 2, rect.height);
- gc.fillRectangle(rect.x, rect.y + rect.height - 2, rect.width, 2);
- }
- gc.setForeground(getForeground());
- }
- return;
- }
-
- // redraw the Border
- drawBorder(gc);
-
- rect.x += borderLeft;
- rect.y += borderTop;
- rect.width -= borderLeft + borderRight;
- rect.height -= borderTop + borderBottom;
- Rectangle clip = gc.getClipping ();
- gc.setClipping(clip.intersection(rect));
-
- // Draw the unselected tabs first.
- for (int i=0; i < items.length; i++) {
- if (i != selectedIndex && event.getBounds().intersects(items[i].getBounds())) {
- items[i].onPaint(gc, false);
- }
- }
- // Selected tab comes last
- if (selectedIndex != -1) {
- items[selectedIndex].onPaint(gc, true);
- }
-
- // draw insertion mark
- if (insertionIndex > -2) {
- gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION));
- if (insertionIndex == -1) {
- Rectangle bounds = items[0].getBounds();
- gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height - 1);
- gc.drawLine(bounds.x - 2, bounds.y, bounds.x + 2, bounds.y);
- gc.drawLine(bounds.x - 1, bounds.y + 1, bounds.x + 1, bounds.y + 1);
- gc.drawLine(bounds.x - 1, bounds.y + bounds.height - 2, bounds.x + 1, bounds.y + bounds.height - 2);
- gc.drawLine(bounds.x - 2, bounds.y + bounds.height - 1, bounds.x + 2, bounds.y + bounds.height - 1);
-
- } else {
- Rectangle bounds = items[insertionIndex].getBounds();
- gc.drawLine(bounds.x + bounds.width, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height - 1);
- gc.drawLine(bounds.x + bounds.width - 2, bounds.y, bounds.x + bounds.width + 2, bounds.y);
- gc.drawLine(bounds.x + bounds.width - 1, bounds.y + 1, bounds.x + bounds.width + 1, bounds.y + 1);
- gc.drawLine(bounds.x + bounds.width - 1, bounds.y + bounds.height - 2, bounds.x + bounds.width + 1, bounds.y + bounds.height - 2);
- gc.drawLine(bounds.x + bounds.width - 2, bounds.y + bounds.height - 1, bounds.x + bounds.width + 2, bounds.y + bounds.height - 1);
- }
- }
-
- gc.setForeground(getForeground());
- gc.setBackground(getBackground());
-}
-private void redrawTabArea(int index) {
- int x = 0, y = 0, width = 0, height = 0;
- if (index == -1) {
- Rectangle area = super.getClientArea();
- if (area.width == 0 || area.height == 0) return;
- width = area.x + area.width - borderLeft - borderRight;
- height = tabHeight + 1; // +1 causes top line between content and tabs to be redrawn
- x = area.x + borderLeft;
- y = area.y + borderTop;
- if (onBottom) {
- y = Math.max(0, area.y + area.height - borderBottom - height);
- }
- } else {
- CTabItem item = items[index];
- x = item.x;
- y = item.y;
- Rectangle area = super.getClientArea();
- width = area.x + area.width - x;
- height = item.height;
- }
- redraw(x, y, width, height, false);
-}
-
-/**
- * Removes the listener.
- *
- * @param listener the listener
- *
- * @exception SWTError
- * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
- */
-public void removeSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- removeListener(SWT.Selection, listener);
- removeListener(SWT.DefaultSelection, listener);
-}
-/**
- * Removes the listener.
- *
- * @param listener the listener
- *
- * @exception SWTError
- * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
- */
-public void removeCTabFolderListener(CTabFolderListener listener) {
- checkWidget();
- if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- if (tabListeners.length == 0) return;
- int index = -1;
- for (int i = 0; i < tabListeners.length; i++) {
- if (listener == tabListeners[i]){
- index = i;
- break;
- }
- }
- if (index == -1) return;
- if (tabListeners.length == 1) {
- tabListeners = new CTabFolderListener[0];
- showClose = false;
- setButtonBounds();
- return;
- }
- CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
- System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
- System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
- tabListeners = newTabListeners;
-}
-
-/**
- * The widget was resized. Adjust the size of the currently selected page.
- */
-private void onResize() {
-
- if (items.length == 0) {
- redraw();
- return;
- }
-
- if (setItemBounds()) {
- redrawTabArea(-1);
- }
-
- Point size = getSize();
- if (oldSize == null) {
- redraw();
- } else {
- if (onBottom && size.y != oldSize.y) {
- redraw();
- } else {
- int x1 = Math.min(size.x, oldSize.x);
- if (size.x != oldSize.x) x1 -= 10;
- int y1 = Math.min(size.y, oldSize.y);
- if (size.y != oldSize.y) y1 -= 10;
- int x2 = Math.max(size.x, oldSize.x);
- int y2 = Math.max(size.y, oldSize.y);
- redraw(0, y1, x2 + 10, y2 - y1, false);
- redraw(x1, 0, x2 - x1, y2, false);
- }
- }
- oldSize = size;
-
- // resize content
- if (selectedIndex != -1) {
- Control control = items[selectedIndex].getControl();
- if (control != null && !control.isDisposed()) {
- control.setBounds(getClientArea());
- }
- }
-}
-
-public void setBackground (Color color) {
- super.setBackground(color);
- background = color;
- // init inactive close button
- inactiveCloseBar.setBackground(color);
-
- // init scroll buttons
- arrowBar.setBackground(color);
-
- //init topRight control
- if (topRight != null)
- topRight.setBackground(color);
-
- // init close button
- if (gradientColors == null) {
- closeBar.setBackground(color);
- }
-}
-/**
- * Specify a gradient of colours to be draw in the background of the selected tab.
- * For example to draw a gradient that varies from dark blue to blue and then to
- * white, use the following call to setBackground:
- * <pre>
- * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
- * display.getSystemColor(SWT.COLOR_BLUE),
- * display.getSystemColor(SWT.COLOR_WHITE),
- * display.getSystemColor(SWT.COLOR_WHITE)},
- * new int[] {25, 50, 100});
- * </pre>
- *
- * @param colors an array of Color that specifies the colors to appear in the gradient
- * in order of appearance left to right. The value <code>null</code> clears the
- * background gradient. The value <code>null</code> can be used inside the array of
- * Color to specify the background color.
- * @param percents an array of integers between 0 and 100 specifying the percent of the width
- * of the widget at which the color should change. The size of the percents array must be one
- * less than the size of the colors array.
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * </ul>
- */
-
-public void setSelectionBackground(Color[] colors, int[] percents) {
- checkWidget();
- if (colors != null) {
- if (percents == null || percents.length != colors.length - 1) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (getDisplay().getDepth() < 15) {
- // Don't use gradients on low color displays
- colors = new Color[] { colors[0] };
- percents = new int[] { };
- }
- for (int i = 0; i < percents.length; i++) {
- if (percents[i] < 0 || percents[i] > 100) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (i > 0 && percents[i] < percents[i-1]) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- }
- }
-
- // Are these settings the same as before?
- if (backgroundImage == null) {
- if ((gradientColors != null) && (colors != null) &&
- (gradientColors.length == colors.length)) {
- boolean same = false;
- for (int i = 0; i < gradientColors.length; i++) {
- if (gradientColors[i] == null) {
- same = colors[i] == null;
- } else {
- same = gradientColors[i].equals(colors[i]);
- }
- if (!same) break;
- }
- if (same) {
- for (int i = 0; i < gradientPercents.length; i++) {
- same = gradientPercents[i] == percents[i];
- if (!same) break;
- }
- }
- if (same) return;
- }
- } else {
- backgroundImage = null;
- }
- // Store the new settings
- if (colors == null) {
- gradientColors = null;
- gradientPercents = null;
- closeBar.setBackground(background);
- } else {
- gradientColors = new Color[colors.length];
- for (int i = 0; i < colors.length; ++i)
- gradientColors[i] = colors[i];
- gradientPercents = new int[percents.length];
- for (int i = 0; i < percents.length; ++i)
- gradientPercents[i] = percents[i];
- if (getDisplay().getDepth() < 15) closeBar.setBackground(background);
- else closeBar.setBackground(gradientColors[gradientColors.length - 1]);
- }
-
- // Refresh with the new settings
- if (selectedIndex > -1) redrawTabArea(selectedIndex);
-}
-
-/**
- * Set the image to be drawn in the background of the selected tab.
- *
- * @param image the image to be drawn in the background
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setSelectionBackground(Image image) {
- checkWidget();
- if (image == backgroundImage) return;
- if (image != null) {
- gradientColors = null;
- gradientPercents = null;
- }
- backgroundImage = image;
- redrawTabArea(selectedIndex);
-}
-/**
- * Toggle the visibility of the border
- *
- * @param show true if the border should be displayed
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setBorderVisible(boolean show) {
- checkWidget();
- if (showBorders == show) return;
-
- showBorders = show;
- if (showBorders) {
- if ((getStyle() & SWT.FLAT) != 0) {
- borderBottom = borderTop = borderLeft = borderRight = 1;
- } else {
- borderLeft = borderTop = 1;
- borderRight = borderBottom = 3;
- }
- } else {
- borderBottom = borderTop = borderLeft = borderRight = 0;
- }
- oldSize = null;
- notifyListeners(SWT.Resize, new Event());
-}
-public void setFont(Font font) {
- checkWidget();
- if (font != null && font.equals(getFont())) return;
- super.setFont(font);
- oldFont = getFont();
- resetTabSize(true);
-}
-/**
- * Set the foreground color of the selected tab.
- *
- * @param color the color of the text displayed in the selected tab
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setSelectionForeground (Color color) {
- checkWidget();
- if (selectionForeground == color) return;
- if (color == null) color = getForeground();
- selectionForeground = color;
- if (selectedIndex > -1) {
- redrawTabArea(selectedIndex);
- }
-}
-/**
- * Display an insert marker before or after the specified tab item.
- * Null will clear the mark.
- *
- * @param item the item with which the mark is associated or null
- *
- * @param after true if the mark should be displayed after the specified item
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setInsertMark(CTabItem item, boolean after) {
- checkWidget();
- int index = -1;
- if (item != null) {
- index = indexOf(item);
- }
- setInsertMark(index, after);
-}
-/**
- * Display an insert marker before or after the specified tab item.
- * -1 will clear the mark.
- *
- * @param item the index of the item with which the mark is associated or null
- *
- * @param after true if the mark should be displayed after the specified item
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setInsertMark(int index, boolean after) {
- checkWidget();
- if (index < -1 || index >= getItemCount()) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
-
- if (index == -1) {
- index = -2;
- } else {
- index = after ? index : --index;
- }
-
- if (insertionIndex == index) return;
- int oldIndex = insertionIndex;
- insertionIndex = index;
- if (index > -1) redrawTabArea(index);
- if (oldIndex > 1) redrawTabArea(oldIndex);
-}
-
-/**
- * Set the selection to the tab at the specified index.
- *
- * @param index the index of the tab item to be selected
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setSelection(int index) {
- checkWidget();
- if (index < 0 || index >= items.length) return;
- if (selectedIndex == index) return;
-
- int oldIndex = selectedIndex;
- selectedIndex = index;
-
- Control control = items[index].control;
- if (control != null && !control.isDisposed()) {
- control.setBounds(getClientArea());
- control.setVisible(true);
- }
-
- if (oldIndex != -1) {
- control = items[oldIndex].control;
- if (control != null && !control.isDisposed()) {
- control.setVisible(false);
- }
- }
- showItem(items[selectedIndex]);
- setButtonBounds();
- redrawTabArea(-1);
-}
-/**
- * Set the control that appears in the top right corner of the tab folder.
- * Typically this is a close button or a composite with a Menu and close button.
- * The topRight control is optional. Setting the top right control to null will remove it from the tab folder.
- *
- * @since 2.1
- *
- * @param control the control to be displayed in the top right corner or null
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
- * </ul>
- */
-public void setTopRight(Control control) {
- checkWidget();
- if (control != null && control.getParent() != this) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- topRight = control;
- resetTabSize(true);
-}
-
-/**
- * Shows the item. If the item is already showing in the receiver,
- * this method simply returns. Otherwise, the items are scrolled until
- * the item is visible.
- *
- * @param item the item to be shown
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
- * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @see CTabFolder#showSelection()
- *
- * @since 2.0
- */
-public void showItem (CTabItem item) {
- checkWidget();
- if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
-
- int index = indexOf(item);
- if (index < topTabIndex) {
- topTabIndex = index;
- setItemLocation();
- redrawTabArea(-1);
- return;
- }
- Rectangle area = getClientArea();
- if (area.width <= 0) {
- topTabIndex = index;
- return;
- }
- int rightEdge = area.x + area.width;
- Rectangle rect = getToolSpace();
- if (rect.width > 0) {
- rightEdge -= rect.width;
- }
- if (item.x + item.width < rightEdge) return;
- setLastItem(index);
-}
-/**
- * Shows the selection. If the selection is already showing in the receiver,
- * this method simply returns. Otherwise, the items are scrolled until
- * the selection is visible.
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @see CTabFolder#showItem(CTabItem)
- *
- * @since 2.0
- *
- */
-public void showSelection () {
- checkWidget ();
- if (selectedIndex != -1) {
- showItem(getSelection());
- }
-}
-
-char getMnemonic (String string) {
- int index = 0;
- int length = string.length ();
- do {
- while ((index < length) && (string.charAt (index) != '&')) index++;
- if (++index >= length) return '\0';
- if (string.charAt (index) != '&') return string.charAt (index);
- index++;
- } while (index < length);
- return '\0';
-}
-/**
- * Set the selection to the tab at the specified item.
- *
- * @param index the tab item to be selected
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_NULL_ARGUMENT - if argument is null</li>
- * </ul>
- */
-public void setSelection(CTabItem item) {
- checkWidget();
- if (item == null)
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- int index = indexOf(item);
- setSelection(index);
-}
-/**
- * Set the selection to the tab at the specified index.
- */
-private void setSelection(int index, boolean notify) {
- int oldSelectedIndex = selectedIndex;
- setSelection(index);
- if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
- Event event = new Event();
- event.item = getItem(selectedIndex);
- notifyListeners(SWT.Selection, event);
- }
-}
-
-private void updateCloseBar() {
- int imageHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN - 4;
- if (imageHeight < 8) return;
-
- if (closeImage != null && closeImage.getBounds().height == imageHeight) return;
-
- if (closeBar != null) closeBar.dispose();
- closeBar = null;
- if (inactiveCloseBar != null) inactiveCloseBar.dispose();
- inactiveCloseBar = null;
- createCloseBar();
-
- ToolItem closeItem = closeBar.getItems()[0];
- ToolItem inactiveCloseItem = inactiveCloseBar.getItems()[0];
-
- if (closeImage != null) closeImage.dispose();
-
- Display display = getDisplay();
- Color foreground = getForeground();
- Color black = display.getSystemColor(SWT.COLOR_BLACK);
- Color background = getBackground();
-
- PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()});
- ImageData imageData = new ImageData(imageHeight, imageHeight, 4, palette);
- imageData.transparentPixel = 1;
- closeImage = new Image(display, imageData);
- GC gc = new GC(closeImage);
- gc.setBackground(background);
- gc.fillRectangle(0, 0, imageHeight, imageHeight);
- gc.setForeground(black);
-
- //draw an 8x7 'x' centered in image
- int h = (imageHeight / 2 )* 2;
- int inset = (h - 8) / 2;
- gc.drawLine( inset, inset, h - inset - 1, h - inset - 1);
- gc.drawLine( inset + 1, inset, h - inset, h - inset - 1);
- gc.drawLine( inset, h - inset - 1, h - inset - 1, inset);
- gc.drawLine( inset + 1, h - inset - 1, h - inset, inset);
-
- gc.dispose();
-
- closeItem.setImage(closeImage);
- inactiveCloseItem.setImage(closeImage);
-}
-private void updateArrowBar() {
-
- int imageHeight = tabHeight - 6;
- if (imageHeight < 10) return;
-
- if (arrowLeftImage != null && arrowLeftImage.getBounds().height == imageHeight) return;
-
- if (arrowBar != null) arrowBar.dispose();
- arrowBar = null;
- if (arrowLeftImage != null) arrowLeftImage.dispose();
- if (arrowRightImage != null) arrowRightImage.dispose();
-
- createArrowBar();
- ToolItem[] items = arrowBar.getItems();
- ToolItem left = items[0];
- ToolItem right = items[1];
-
- Display display = getDisplay();
- Color foreground = getForeground();
- Color black = display.getSystemColor(SWT.COLOR_BLACK);
- Color background = getBackground();
-
- PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()});
- ImageData imageData = new ImageData(7, imageHeight, 4, palette);
- imageData.transparentPixel = 1;
- arrowLeftImage = new Image(display, imageData);
- GC gc = new GC(arrowLeftImage);
- gc.setBackground(background);
- gc.fillRectangle(0, 0, 7, imageHeight);
- gc.setBackground(black);
- //draw a 10x5 '<' centered vertically in image
- int h = (imageHeight / 2 )* 2;
- int midpoint = h / 2 - 1;
- int[] pointArr = new int[] {6, midpoint - 5,
- 1, midpoint,
- 6, midpoint + 5,};
- gc.fillPolygon(pointArr);
- gc.dispose();
-
- palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()});
- imageData = new ImageData(7, imageHeight, 4, palette);
- imageData.transparentPixel = 1;
- arrowRightImage = new Image(display, imageData);
- gc = new GC(arrowRightImage);
- gc.setBackground(background);
- gc.fillRectangle(0, 0, 7, imageHeight);
- gc.setBackground(black);
- //draw a 10x5 '>' centered vertically in image
- pointArr = new int[] {1, midpoint - 5,
- 6, midpoint,
- 1, midpoint + 5,};
- gc.fillPolygon(pointArr);
- gc.dispose();
-
- left.setImage(arrowLeftImage);
- right.setImage(arrowRightImage);
-}
-
-private void onMouseDoubleClick(Event event) {
- Event e = new Event();
- e.item = getItem(new Point(event.x, event.y));
- notifyListeners(SWT.DefaultSelection, e);
-}
-/**
- * A mouse button was pressed down.
- * If a tab was hit select the tab.
- */
-private void onMouseDown(Event event) {
- for (int i=0; i<items.length; i++) {
- if (items[i].getBounds().contains(new Point(event.x, event.y))) {
- if (i == selectedIndex) {
- showSelection();
- return;
- }
- forceFocus();
- setSelection(i, true);
- if (isFocusControl()) setFocus();
- return;
- }
- }
-}
-
-private void onMouseExit(Event event) {
- Rectangle inactiveBounds = inactiveCloseBar.getBounds();
- if (inactiveBounds.contains(event.x, event.y)) return;
- inactiveCloseBar.setVisible(false);
- inactiveItem = null;
-
- showToolTip = false;
- toolTipItem = null;
- if (tip != null && !tip.isDisposed() && tip.isVisible()) tip.setVisible(false);
-}
-
-private void onMouseHover(Event event) {
- if (tip == null || tip.isDisposed()) return;
- showToolTip = true;
- showToolTip(event.x, event.y);
-}
-private void showToolTip (int x, int y) {
- CTabItem item = getItem(new Point (x, y));
- if (item != null) {
- if (item == toolTipItem) return;
- toolTipItem = item;
- String tooltip = item.getToolTipText();
- if (tooltip != null) {
- Display display = tip.getDisplay();
- label.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
- label.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
- label.setText(tooltip);
- Point labelSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- labelSize.x += 2; labelSize.y += 2;
- label.setSize(labelSize);
- tip.pack();
- /*
- * On some platforms, there is a minimum size for a shell
- * which may be greater than the label size.
- * To avoid having the background of the tip shell showing
- * around the label, force the label to fill the entire client area.
- */
- Rectangle area = tip.getClientArea();
- label.setSize(area.width, area.height);
- /*
- * Position the tooltip and ensure that it is not located off
- * the screen.
- */
- Point pt = new Point(item.x + item.width / 4, item.y + item.height + 2);
- pt = toDisplay(pt);
- Rectangle rect = display.getBounds();
- Point tipSize = tip.getSize();
- pt.x = Math.max (0, Math.min (pt.x, rect.width - tipSize.x));
- pt.y = Math.max (0, Math.min (pt.y, rect.height - tipSize.y));
- tip.setLocation(pt);
- tip.setVisible(true);
- return;
- }
- }
-
- toolTipItem = null;
- if (tip != null && !tip.isDisposed() && tip.isVisible()) tip.setVisible(false);
-}
-private void onMouseMove(Event event) {
- if (showToolTip) {
- showToolTip(event.x, event.y);
- }
-
- if (!showClose) return;
-
- CTabItem item = null;
- for (int i=0; i<items.length; i++) {
- Rectangle rect = items[i].getBounds();
- if (rect.contains(new Point(event.x, event.y))) {
- item = items[i];
- break;
- }
- }
- if (item == inactiveItem) return;
-
- inactiveCloseBar.setVisible(false);
- inactiveItem = null;
-
- if (item == null || item == getSelection()) return;
-
- int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
- Point size = inactiveCloseBar.computeSize(SWT.DEFAULT, toolbarHeight);
- int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
- int y = item.y + Math.max(0, (item.height - toolbarHeight)/2);
- Rectangle toolspace = getToolSpace();
- Point folderSize = getSize();
- if ((toolspace.width == 0 || x < toolspace.x) && x + size.x < folderSize.x - borderRight) {
- inactiveCloseBar.setBounds(x, y, size.x, toolbarHeight);
- inactiveCloseBar.setVisible(true);
- inactiveItem = item;
- }
-}
-private void onTraverse (Event event) {
- switch (event.detail) {
- case SWT.TRAVERSE_ESCAPE:
-// TEMPORARY CODE See bug report 17372
-// case SWT.TRAVERSE_RETURN:
- case SWT.TRAVERSE_TAB_NEXT:
- case SWT.TRAVERSE_TAB_PREVIOUS:
- event.doit = true;
- break;
- case SWT.TRAVERSE_MNEMONIC:
- event.doit = onMnemonic(event);
- if (event.doit) event.detail = SWT.TRAVERSE_NONE;
- break;
- case SWT.TRAVERSE_PAGE_NEXT:
- case SWT.TRAVERSE_PAGE_PREVIOUS:
- event.doit = onPageTraversal(event);
- if (event.doit) event.detail = SWT.TRAVERSE_NONE;
- break;
- }
-}
-
-private boolean onPageTraversal(Event event) {
- int count = getItemCount ();
- if (count == 0) return false;
- int index = getSelectionIndex ();
- if (index == -1) {
- index = 0;
- } else {
- int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
- index = (index + offset + count) % count;
- }
- setSelection (index, true);
- return true;
-}
-
-/**
- * Answer true if not all tabs can be visible in the receive
- * thus requiring the scroll buttons to be visible.
- */
-private boolean scroll_leftVisible() {
- return topTabIndex > 0;
-}
-
-/**
- * Answer true if not all tabs can be visible in the receive
- * thus requiring the scroll buttons to be visible.
- */
-private boolean scroll_rightVisible() {
- // only show Scroll buttons if there is more than one item
- // and if we are not already at the last item
- if (items.length < 2) return false;
- Rectangle area = getClientArea();
- int rightEdge = area.x + area.width;
- if (rightEdge <= 0) return false;
- if (topTabIndex > 0) {
- rightEdge -= arrowBar.getSize().x;
- }
- if (topRight != null) {
- rightEdge -= topRight.getSize().x;
- }
- CTabItem item = items[items.length-1];
- return (item.x + item.width > rightEdge);
-}
-
-/**
- * Scroll the tab items to the left.
- */
-private void scroll_scrollLeft() {
- if (items.length == 0) return;
- setLastItem(topTabIndex - 1);
-}
-
-/**
- * Scroll the tab items to the right.
- */
-private void scroll_scrollRight() {
- int lastIndex = getLastItem();
- topTabIndex = lastIndex + 1;
- setItemLocation();
- correctLastItem();
- redrawTabArea(-1);
-}
-private boolean correctLastItem() {
- Rectangle area = getClientArea();
- int rightEdge = area.x + area.width;
- if (rightEdge <= 0) return false;
- Rectangle toolspace = getToolSpace();
- if (toolspace.width > 0) {
- rightEdge -= toolspace.width;
- }
- CTabItem item = items[items.length - 1];
- if (item.x + item.width < rightEdge) {
- setLastItem(items.length - 1);
- return true;
- }
- return false;
-}
-/**
- * Specify a fixed height for the tab items. If no height is specified,
- * the default height is the height of the text or the image, whichever
- * is greater. Specifying a height of 0 will revert to the default height.
- *
- * @param height the pixel value of the height or 0
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
- * </ul>
- */
-public void setTabHeight(int height) {
- checkWidget();
- if (height < 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- fixedTabHeight = true;
- if (tabHeight == height) return;
- tabHeight = height;
- oldSize = null;
- notifyListeners(SWT.Resize, new Event());
-}
-void resetTabSize(boolean checkHeight){
- int oldHeight = tabHeight;
- if (!fixedTabHeight && checkHeight) {
- int tempHeight = 0;
- GC gc = new GC(this);
- for (int i=0; i < items.length; i++) {
- tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc));
- }
- gc.dispose();
- if (topRight != null) tempHeight = Math.max(tempHeight, topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
- tabHeight = tempHeight;
- }
-
- if (tabHeight != oldHeight){
- oldSize = null;
- notifyListeners(SWT.Resize, new Event());
- } else {
- setItemBounds();
- redraw();
- }
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.accessibility.*;
+
+/**
+ * Instances of this class implement the notebook user interface
+ * metaphor. It allows the user to select a notebook page from
+ * set of pages.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>CTabItem</code>.
+ * <code>Control</code> children are created and then set into a
+ * tab item using <code>CTabItem#setControl</code>.
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>TOP, BOTTOM, FLAT</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * <dd>"CTabFolder"</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles TOP and BOTTOM
+ * may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class CTabFolder extends Composite {
+
+ /**
+ * marginWidth specifies the number of pixels of horizontal margin
+ * that will be placed along the left and right edges of the form.
+ *
+ * The default value is 0.
+ */
+ public int marginWidth = 0;
+ /**
+ * marginHeight specifies the number of pixels of vertical margin
+ * that will be placed along the top and bottom edges of the form.
+ *
+ * The default value is 0.
+ */
+ public int marginHeight = 0;
+
+ /**
+ * Color of innermost line of drop shadow border.
+ */
+ public static RGB borderInsideRGB = new RGB (132, 130, 132);
+ /**
+ * Color of middle line of drop shadow border.
+ */
+ public static RGB borderMiddleRGB = new RGB (143, 141, 138);
+ /**
+ * Color of outermost line of drop shadow border.
+ */
+ public static RGB borderOutsideRGB = new RGB (171, 168, 165);
+
+ /*
+ * A multiple of the tab height that specifies the minimum width to which a tab
+ * will be compressed before scrolling arrows are used to navigate the tabs.
+ */
+ public int MIN_TAB_WIDTH = 3;
+
+ /* sizing, positioning */
+ int xClient, yClient;
+ boolean onBottom = false;
+ boolean fixedTabHeight;
+ int tabHeight;
+
+ /* item management */
+ private CTabItem items[] = new CTabItem[0];
+ private int selectedIndex = -1;
+ int topTabIndex = -1; // index of the left most visible tab.
+
+ /* External Listener management */
+ private CTabFolderListener[] tabListeners = new CTabFolderListener[0];
+
+ /* Color appearance */
+ Image backgroundImage;
+ Color[] gradientColors;
+ int[] gradientPercents;
+ Color selectionForeground;
+ Color background;
+
+ // internal constants
+ private static final int DEFAULT_WIDTH = 64;
+ private static final int DEFAULT_HEIGHT = 64;
+
+ // scrolling arrows
+ private ToolBar arrowBar;
+ private Image arrowLeftImage;
+ private Image arrowRightImage;
+
+ private Control topRight;
+
+ // close button
+ boolean showClose = false;
+ private Image closeImage;
+ ToolBar closeBar;
+ private ToolBar inactiveCloseBar;
+ private CTabItem inactiveItem;
+
+ // borders
+ boolean showBorders = false;
+ private int borderBottom = 0;
+ private int borderLeft = 0;
+ private int borderRight = 0;
+ private int borderTop = 0;
+ private Color borderColor1;
+ private Color borderColor2;
+ private Color borderColor3;
+
+ // when disposing CTabFolder, don't try to layout the items or
+ // change the selection as each child is destroyed.
+ private boolean inDispose = false;
+
+ // keep track of size changes in order to redraw only affected area
+ // on Resize
+ private Point oldSize;
+ private Font oldFont;
+
+ // insertion marker
+ int insertionIndex = -2; // Index of insert marker. Marker always shown after index.
+ // -2 means no insert marker
+
+ // tool tip
+ private Shell tip;
+ private Label label;
+ private boolean showToolTip = false;
+ private CTabItem toolTipItem;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#TOP
+ * @see SWT#BOTTOM
+ * @see SWT#FLAT
+ * @see #getStyle
+ */
+public CTabFolder(Composite parent, int style) {
+ super(parent, checkStyle (style));
+
+ onBottom = (getStyle() & SWT.BOTTOM) != 0;
+
+ borderColor1 = new Color(getDisplay(), borderInsideRGB);
+ borderColor2 = new Color(getDisplay(), borderMiddleRGB);
+ borderColor3 = new Color(getDisplay(), borderOutsideRGB);
+
+ // tool tip support
+ tip = new Shell (getShell(), SWT.ON_TOP);
+ label = new Label (tip, SWT.CENTER);
+
+ // Add all listeners
+ Listener listener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.Dispose: onDispose(); break;
+ case SWT.Paint: onPaint(event); break;
+ case SWT.Resize: onResize(); break;
+ case SWT.MouseDoubleClick: onMouseDoubleClick(event); break;
+ case SWT.MouseDown: onMouseDown(event); break;
+ case SWT.MouseExit: onMouseExit(event); break;
+ case SWT.MouseHover: onMouseHover(event); break;
+ case SWT.MouseMove: onMouseMove(event); break;
+ case SWT.FocusIn: onFocus(event); break;
+ case SWT.FocusOut: onFocus(event); break;
+ case SWT.KeyDown: onKeyDown(event); break;
+ case SWT.Traverse: onTraverse(event); break;
+ }
+ }
+ };
+
+ int[] folderEvents = new int[]{
+ SWT.Dispose,
+ SWT.Paint,
+ SWT.Resize,
+ SWT.MouseDoubleClick,
+ SWT.MouseDown,
+ SWT.MouseExit,
+ SWT.MouseHover,
+ SWT.MouseMove,
+ SWT.FocusIn,
+ SWT.FocusOut,
+ SWT.KeyDown,
+ SWT.Traverse,
+ };
+ for (int i = 0; i < folderEvents.length; i++) {
+ addListener(folderEvents[i], listener);
+ }
+
+ createArrowBar();
+ createCloseBar();
+
+ setBorderVisible((style & SWT.BORDER) != 0);
+
+ initAccessible();
+
+}
+private static int checkStyle (int style) {
+ int mask = SWT.TOP | SWT.BOTTOM | SWT.FLAT;
+ style = style & mask;
+ // TOP and BOTTOM are mutually exlusive.
+ // TOP is the default
+ if ((style & SWT.TOP) != 0)
+ style = style & ~(SWT.TOP | SWT.BOTTOM) | SWT.TOP;
+ // reduce the flash by not redrawing the entire area on a Resize event
+ style |= SWT.NO_REDRAW_RESIZE;
+ return style;
+}
+/**
+* Adds the listener to receive events.
+* <p>
+*
+* @param listener the listener
+*
+* @exception SWTError <ul>
+* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+* <li>ERROR_NULL_ARGUMENT when listener is null</li>
+* </ul>
+*/
+public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+ addListener(SWT.DefaultSelection, typedListener);
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when a tab item is closed.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see CTabFolderListener
+ * @see #removeCTabFolderListener
+ */
+public void addCTabFolderListener(CTabFolderListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ // add to array
+ CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
+ System.arraycopy(tabListeners, 0, newTabListeners, 0, tabListeners.length);
+ tabListeners = newTabListeners;
+ tabListeners[tabListeners.length - 1] = listener;
+ showClose = true;
+ setButtonBounds();
+}
+private void closeNotify(CTabItem item, int time) {
+ if (item == null) return;
+
+ CTabFolderEvent event = new CTabFolderEvent(this);
+ event.widget = this;
+ event.time = time;
+ event.item = item;
+ event.doit = true;
+ if (tabListeners != null) {
+ for (int i = 0; i < tabListeners.length; i++) {
+ tabListeners[i].itemClosed(event);
+ }
+ }
+ if (event.doit) {
+ item.dispose();
+ }
+}
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget();
+ int minWidth = 0;
+ int minHeight = 0;
+
+ // preferred width of tab area to show all tabs
+ GC gc = new GC(this);
+ for (int i = 0; i < items.length; i++) {
+ minWidth += items[i].preferredWidth(gc);
+ }
+ gc.dispose();
+
+ // preferred size of controls in tab items
+ for (int i = 0; i < items.length; i++) {
+ Control control = items[i].getControl();
+ if (control != null && !control.isDisposed()){
+ Point size = control.computeSize (wHint, hHint);
+ minWidth = Math.max (minWidth, size.x);
+ minHeight = Math.max (minHeight, size.y);
+ }
+ }
+ if (minWidth == 0) minWidth = DEFAULT_WIDTH;
+ if (minHeight == 0) minHeight = DEFAULT_HEIGHT;
+
+ if (wHint != SWT.DEFAULT) minWidth = wHint;
+ if (hHint != SWT.DEFAULT) minHeight = hHint;
+
+ Rectangle trim = computeTrim(0, 0, minWidth, minHeight);
+ return new Point (trim.width, trim.height);
+}
+public Rectangle computeTrim (int x, int y, int width, int height) {
+ checkWidget();
+ if (items.length == 0) {
+ if (!showBorders) return new Rectangle(x, y, width, height);
+ int trimX = x - borderRight - 1;
+ int trimY = y - borderBottom - 1;
+ int trimWidth = width + borderRight + 2;
+ int trimHeight = height + borderBottom + 2;
+ return new Rectangle (trimX, trimY, trimWidth, trimHeight);
+ } else {
+ int trimX = x - marginWidth - borderLeft;
+ int trimY = y - marginHeight - tabHeight - borderTop - 1;
+ // -1 is for the line at the bottom of the tabs
+ if (onBottom) {
+ trimY = y - marginHeight - borderTop;
+ }
+ int trimWidth = width + borderLeft + borderRight + 2*marginWidth;
+ int trimHeight = height + borderTop + borderBottom + 2*marginHeight + tabHeight + 1;
+ return new Rectangle (trimX, trimY, trimWidth, trimHeight);
+ }
+}
+/**
+ * Create the specified item at 'index'.
+ */
+void createItem (CTabItem item, int index) {
+ if (0 > index || index > getItemCount ()){
+ SWT.error (SWT.ERROR_INVALID_RANGE);
+ }
+ // grow by one and rearrange the array.
+ CTabItem[] newItems = new CTabItem [items.length + 1];
+ System.arraycopy(items, 0, newItems, 0, index);
+ newItems[index] = item;
+ System.arraycopy(items, index, newItems, index + 1, items.length - index);
+ items = newItems;
+
+ item.parent = this;
+
+ if (selectedIndex >= index) {
+ selectedIndex ++;
+ }
+ if (items.length == 1) {
+ topTabIndex = 0;
+ resetTabSize(true);
+ } else {
+ setItemBounds();
+ showItem(item);
+ }
+
+ if (items.length == 1) {
+ redraw();
+ } else {
+ redrawTabArea(-1);
+ }
+}
+
+private void createArrowBar() {
+ // create arrow buttons for scrolling
+ arrowBar = new ToolBar(this, SWT.FLAT);
+ arrowBar.setVisible(false);
+ arrowBar.setBackground(background);
+ ToolItem scrollLeft = new ToolItem(arrowBar, SWT.PUSH);
+ scrollLeft.setEnabled(false);
+ ToolItem scrollRight = new ToolItem(arrowBar, SWT.PUSH);
+ scrollRight.setEnabled(false);
+
+ scrollLeft.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ scroll_scrollLeft();
+ }
+ });
+ scrollRight.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ scroll_scrollRight();
+ }
+ });
+
+}
+private void createCloseBar() {
+ closeBar = new ToolBar(this, SWT.FLAT);
+ closeBar.setVisible(false);
+ if (gradientColors != null && gradientColors.length > 0) {
+ closeBar.setBackground(gradientColors[gradientColors.length - 1]);
+ } else {
+ closeBar.setBackground(background);
+ }
+ ToolItem closeItem = new ToolItem(closeBar, SWT.PUSH);
+
+ inactiveCloseBar = new ToolBar(this, SWT.FLAT);
+ inactiveCloseBar.setVisible(false);
+ inactiveCloseBar.setBackground(background);
+ ToolItem inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH);
+
+ closeItem.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ closeNotify(getSelection(), event.time);
+ }
+ });
+ inactiveCloseItem.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ closeNotify(inactiveItem, event.time);
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+ }
+ });
+ inactiveCloseBar.addListener (SWT.MouseExit, new Listener() {
+ public void handleEvent(Event event) {
+ if (inactiveItem != null) {
+ Rectangle itemBounds = inactiveItem.getBounds();
+ if (itemBounds.contains(event.x, event.y)) return;
+ }
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+ }
+ });
+
+}
+/**
+ * Destroy the specified item.
+ */
+void destroyItem (CTabItem item) {
+ if (inDispose) return;
+
+ int index = indexOf(item);
+ if (index == -1) return; // should this trigger an error?
+
+ insertionIndex = -2;
+
+ if (items.length == 1) {
+ items = new CTabItem[0];
+ selectedIndex = -1;
+ topTabIndex = 0;
+
+ Control control = item.getControl();
+ if (control != null && !control.isDisposed()) {
+ control.setVisible(false);
+ }
+ closeBar.setVisible(false);
+ if (!fixedTabHeight) tabHeight = 0;
+ redraw();
+ return;
+ }
+
+ // shrink by one and rearrange the array.
+ CTabItem[] newItems = new CTabItem [items.length - 1];
+ System.arraycopy(items, 0, newItems, 0, index);
+ System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
+ items = newItems;
+
+ if (topTabIndex == items.length) {
+ --topTabIndex;
+ }
+
+ // move the selection if this item is selected
+ if (selectedIndex == index) {
+ Control control = item.getControl();
+ if (control != null && !control.isDisposed()) {
+ control.setVisible(false);
+ }
+ selectedIndex = -1;
+ setSelection(Math.max(0, index - 1), true);
+ } else if (selectedIndex > index) {
+ selectedIndex --;
+ }
+
+ setItemBounds();
+ redrawTabArea(-1);
+}
+private void onKeyDown(Event e) {
+ if (e.keyCode == SWT.ARROW_LEFT) {
+ if (selectedIndex > 0) {
+ setSelection(selectedIndex - 1, true);
+ }
+ }
+ if (e.keyCode == SWT.ARROW_RIGHT) {
+ if (selectedIndex < items.length - 1) {
+ setSelection(selectedIndex + 1, true);
+ }
+ }
+}
+/**
+ * Dispose the items of the receiver
+ */
+private void onDispose() {
+ /*
+ * Usually when an item is disposed, destroyItem will change the size of the items array,
+ * reset the bounds of all the tabs and manage the widget associated with the tab.
+ * Since the whole folder is being disposed, this is not necessary. For speed
+ * the inDispose flag is used to skip over this part of the item dispose.
+ */
+ inDispose = true;
+
+ int length = items.length;
+ for (int i = 0; i < length; i++) {
+ if (items[i] != null) {
+ items[i].dispose();
+ }
+ }
+
+ // clean up resources
+ if (tip != null && !tip.isDisposed()) {
+ tip.dispose();
+ tip = null;
+ label = null;
+ }
+
+ if (arrowLeftImage != null) arrowLeftImage.dispose();
+ arrowLeftImage = null;
+ if (arrowRightImage != null) arrowRightImage.dispose();
+ arrowRightImage = null;
+ if (closeImage != null) closeImage.dispose();
+ closeImage = null;
+
+ gradientColors = null;
+ gradientPercents = null;
+ backgroundImage = null;
+
+ if (borderColor1 != null) borderColor1.dispose();
+ borderColor1 = null;
+
+ if (borderColor2 != null) borderColor2.dispose();
+ borderColor2 = null;
+
+ if (borderColor3 != null) borderColor3.dispose();
+ borderColor3 = null;
+}
+private void onFocus(Event e) {
+ checkWidget();
+ if (selectedIndex >= 0) {
+ redrawTabArea(selectedIndex);
+ } else {
+ setSelection(0, true);
+ }
+}
+/**
+ * Draw a border around the receiver.
+ */
+private void drawBorder(GC gc) {
+
+ Rectangle d = super.getClientArea();
+
+ if (showBorders) {
+ if ((getStyle() & SWT.FLAT) != 0) {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height - 1);
+ } else {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height - 3);
+
+ gc.setForeground(borderColor2);
+ gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y + d.height - 2);
+ gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y + d.height - 1);
+
+ gc.setForeground(borderColor3);
+ gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y + d.height - 1);
+ gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y + d.height - 2);
+
+ // fill in corners with parent's background
+ gc.setForeground(getParent().getBackground());
+ gc.drawLine(d.x + d.width - 2, d.y, d.x + d.width - 1, d.y);
+ gc.drawLine(d.x + d.width - 1, d.y + 1, d.x + d.width - 1, d.y + 1);
+
+ gc.drawLine(d.x, d.y + d.height - 2, d.x, d.y + d.height - 2);
+ gc.drawLine(d.x, d.y + d.height - 1, d.x + 1, d.y + d.height - 1);
+
+ gc.drawLine(d.x + d.width - 1, d.y + d.height - 1, d.x + d.width - 1, d.y + d.height - 1);
+ }
+
+ }
+
+ // draw a separator line
+ if (items.length > 0) {
+ int lineY = d.y + borderTop + tabHeight;
+ if (onBottom) {
+ lineY = d.y + d.height - borderBottom - tabHeight - 1;
+ }
+ gc.setForeground(borderColor1);
+ gc.drawLine(d.x + borderLeft, lineY, d.x + d.width - borderRight, lineY);
+ }
+
+ gc.setForeground(getForeground());
+}
+public Rectangle getClientArea() {
+ checkWidget();
+ Point size = getSize();
+ if (items.length == 0) {
+ if (!showBorders) return super.getClientArea();
+ int width = size.x - borderRight - 2;
+ int height = size.y - borderBottom - 2;
+ return new Rectangle(borderRight + 1, borderBottom + 1, width, height);
+ } else {
+ int width = size.x - 2*marginWidth - borderLeft - borderRight;
+ int height = size.y - 2*marginHeight - borderTop - borderBottom - tabHeight - 1;
+ return new Rectangle(xClient, yClient, width, height);
+ }
+}
+/**
+ * Returns the height of the tab
+ *
+ * @return the height of the tab
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * </ul>
+ */
+public int getTabHeight(){
+ checkWidget();
+ return tabHeight;
+}
+
+/**
+ * Return the tab that is located at the specified index.
+ *
+ * @return the item at the specified index
+ */
+public CTabItem getItem (int index) {
+ //checkWidget();
+ if (index < 0 || index >= items.length)
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ return items [index];
+}
+/**
+* Gets the item at a point in the widget.
+* <p>
+*
+* @return the item at a point
+*/
+public CTabItem getItem (Point pt) {
+ //checkWidget();
+ if (items.length == 0) return null;
+ int lastItem = getLastItem();
+ lastItem = Math.min(items.length - 1, lastItem + 1);
+ for (int i = topTabIndex; i <= lastItem; i++) {
+ Rectangle bounds = items[i].getBounds();
+ if (bounds.contains(pt)) return items[i];
+ }
+ return null;
+}
+/**
+ * Return the number of tabs in the folder.
+ *
+ * @return the number of tabs in the folder
+ */
+public int getItemCount(){
+ //checkWidget();
+ return items.length;
+}
+/**
+ * Return the tab items.
+ *
+ * @return the tab items
+ */
+public CTabItem [] getItems() {
+ //checkWidget();
+ CTabItem[] tabItems = new CTabItem [items.length];
+ System.arraycopy(items, 0, tabItems, 0, items.length);
+ return tabItems;
+}
+
+private int getLastItem(){
+ if (items.length == 0) return -1;
+ Rectangle area = getClientArea();
+ if (area.width <= 0) return 0;
+ Rectangle toolspace = getToolSpace();
+ if (toolspace.width == 0) return items.length -1;
+ int width = area.width - toolspace.width;
+ int index = topTabIndex;
+ int tabWidth = items[index].width;
+ while (index < items.length - 1) {
+ tabWidth += items[index + 1].width;
+ if (tabWidth > width) break;
+ index++;
+ }
+ return index;
+}
+/**
+ * Return the selected tab item, or an empty array if there
+ * is no selection.
+ *
+ * @return the selected tab item
+ */
+public CTabItem getSelection() {
+ //checkWidget();
+ if (selectedIndex == -1) return null;
+ return items[selectedIndex];
+}
+/**
+ * Return the index of the selected tab item, or -1 if there
+ * is no selection.
+ *
+ * @return the index of the selected tab item or -1
+ */
+public int getSelectionIndex() {
+ //checkWidget();
+ return selectedIndex;
+}
+private Rectangle getToolSpace() {
+ boolean showArrows = scroll_leftVisible() || scroll_rightVisible();
+ if (!showArrows && topRight == null) return new Rectangle(0, 0, 0, 0);
+ Rectangle toolspace;
+ if (showArrows) {
+ toolspace = arrowBar.getBounds();
+ toolspace.width += borderRight;
+ if (topRight != null) toolspace.width += topRight.getSize().x;
+ } else {
+ toolspace = topRight.getBounds();
+ toolspace.width += borderRight;
+ }
+ return toolspace;
+}
+/**
+ * Returns the control in the top right corner of the tab folder.
+ * Typically this is a close button or a composite with a menu and close button.
+ *
+ * @since 2.1
+ *
+ * @return the control in the top right corner of the tab folder or null
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * </ul>
+ */
+public Control getTopRight() {
+ checkWidget();
+ return topRight;
+}
+
+/**
+ * Return the index of the specified tab or -1 if the tab is not
+ * in the receiver.
+ *
+ * @return the index of the specified tab item or -1
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_NULL_ARGUMENT when the item is null</li>
+ * </ul>
+ */
+public int indexOf(CTabItem item) {
+ //checkWidget();
+ if (item == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] == item) return i;
+ }
+ return -1;
+}
+
+private void initAccessible() {
+ final Accessible accessible = getAccessible();
+ accessible.addAccessibleListener(new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ String name = null;
+ int childID = e.childID;
+ if (childID >= 0 && childID < items.length) {
+ name = items[childID].getText();
+ int index = name.indexOf('&');
+ if (index > 0) {
+ name = name.substring(0, index) + name.substring(index + 1);
+ }
+ }
+ e.result = name;
+ }
+
+ public void getHelp(AccessibleEvent e) {
+ String help = null;
+ int childID = e.childID;
+ if (childID == ACC.CHILDID_SELF) {
+ help = getToolTipText();
+ } else if (childID >= 0 && childID < items.length) {
+ help = items[childID].getToolTipText();
+ }
+ e.result = help;
+ }
+
+ public void getKeyboardShortcut(AccessibleEvent e) {
+ String shortcut = null;
+ int childID = e.childID;
+ if (childID >= 0 && childID < items.length) {
+ String text = items[childID].getText();
+ if (text != null) {
+ char mnemonic = getMnemonic(text);
+ if (mnemonic != '\0') {
+ shortcut = "Alt+"+mnemonic; //$NON-NLS-1$
+ }
+ }
+ }
+ e.result = shortcut;
+ }
+ });
+
+ accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
+ public void getChildAtPoint(AccessibleControlEvent e) {
+ Point testPoint = toControl(new Point(e.x, e.y));
+ int childID = ACC.CHILDID_NONE;
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].getBounds().contains(testPoint)) {
+ childID = i;
+ break;
+ }
+ }
+ if (childID == ACC.CHILDID_NONE) {
+ Rectangle location = getBounds();
+ location.height = location.height - getClientArea().height;
+ if (location.contains(testPoint)) {
+ childID = ACC.CHILDID_SELF;
+ }
+ }
+ e.childID = childID;
+ }
+
+
+ public void getLocation(AccessibleControlEvent e) {
+ Rectangle location = null;
+ int childID = e.childID;
+ if (childID == ACC.CHILDID_SELF) {
+ location = getBounds();
+ }
+ if (childID >= 0 && childID < items.length) {
+ location = items[childID].getBounds();
+ }
+ if (location != null) {
+ Point pt = toDisplay(new Point(location.x, location.y));
+ e.x = pt.x;
+ e.y = pt.y;
+ e.width = location.width;
+ e.height = location.height;
+ }
+ }
+
+ public void getChildCount(AccessibleControlEvent e) {
+ e.detail = items.length;
+ }
+
+ public void getDefaultAction(AccessibleControlEvent e) {
+ String action = null;
+ int childID = e.childID;
+ if (childID >= 0 && childID < items.length) {
+ action = "Switch"; //$NON-NLS-1$
+ }
+ e.result = action;
+ }
+
+ public void getFocus(AccessibleControlEvent e) {
+ int childID = ACC.CHILDID_NONE;
+ if (isFocusControl()) {
+ if (selectedIndex == -1) {
+ childID = ACC.CHILDID_SELF;
+ } else {
+ childID = selectedIndex;
+ }
+ }
+ e.childID = childID;
+ }
+
+ public void getRole(AccessibleControlEvent e) {
+ int role = 0;
+ int childID = e.childID;
+ if (childID == ACC.CHILDID_SELF) {
+ role = ACC.ROLE_TABFOLDER;
+ } else if (childID >= 0 && childID < items.length) {
+ role = ACC.ROLE_TABITEM;
+ }
+ e.detail = role;
+ }
+
+ public void getSelection(AccessibleControlEvent e) {
+ e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
+ }
+
+ public void getState(AccessibleControlEvent e) {
+ int state = 0;
+ int childID = e.childID;
+ if (childID == ACC.CHILDID_SELF) {
+ state = ACC.STATE_NORMAL;
+ } else if (childID >= 0 && childID < items.length) {
+ state = ACC.STATE_SELECTABLE;
+ if (isFocusControl()) {
+ state |= ACC.STATE_FOCUSABLE;
+ }
+ if (selectedIndex == childID) {
+ state |= ACC.STATE_SELECTED;
+ if (isFocusControl()) {
+ state |= ACC.STATE_FOCUSED;
+ }
+ }
+ }
+ e.detail = state;
+ }
+
+ public void getChildren(AccessibleControlEvent e) {
+ Object[] children = new Object[items.length];
+ for (int i = 0; i < items.length; i++) {
+ children[i] = new Integer(i);
+ }
+ e.children = children;
+ }
+ });
+
+ addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ if (isFocusControl()) {
+ if (selectedIndex == -1) {
+ accessible.setFocus(ACC.CHILDID_SELF);
+ } else {
+ accessible.setFocus(selectedIndex);
+ }
+ }
+ }
+ });
+
+ addListener(SWT.FocusIn, new Listener() {
+ public void handleEvent(Event event) {
+ if (selectedIndex == -1) {
+ accessible.setFocus(ACC.CHILDID_SELF);
+ } else {
+ accessible.setFocus(selectedIndex);
+ }
+ }
+ });
+}
+
+private void setButtonBounds() {
+
+ updateArrowBar();
+ updateCloseBar();
+
+ Rectangle area = super.getClientArea();
+
+ int offset = 0;
+ if (topRight != null) {
+ Point size = topRight.computeSize(SWT.DEFAULT, tabHeight);
+ int x = area.x + area.width - borderRight - size.x;
+ int y = onBottom ? area.y + area.height - borderBottom - size.y : area.y + borderTop;
+ topRight.setBounds(x, y, size.x, size.y);
+ offset = size.x;
+ }
+ boolean leftVisible = scroll_leftVisible();
+ boolean rightVisible = scroll_rightVisible();
+ if (leftVisible || rightVisible) {
+ Point size = arrowBar.computeSize(SWT.DEFAULT, tabHeight);
+ int x = area.x + area.width - borderRight - size.x - offset;
+ int y = (onBottom) ? area.y + area.height - borderBottom - size.y : area.y + borderTop;
+
+ arrowBar.setBounds(x, y, size.x, size.y);
+ ToolItem[] items = arrowBar.getItems();
+ items[0].setEnabled(leftVisible);
+ items[1].setEnabled(rightVisible);
+ arrowBar.setVisible(true);
+ } else {
+ arrowBar.setVisible(false);
+ }
+
+ // When the close button is right at the edge of the Tab folder, hide it because
+ // otherwise it may block off a part of the border on the right
+ if (showClose) {
+ inactiveCloseBar.setVisible(false);
+ CTabItem item = getSelection();
+ if (item == null) {
+ closeBar.setVisible(false);
+ } else {
+ int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
+ Point size = closeBar.computeSize(SWT.DEFAULT, toolbarHeight);
+ int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
+ int y = item.y + Math.max(0, (item.height - toolbarHeight)/2);
+ closeBar.setBounds(x, y, size.x, toolbarHeight);
+ Rectangle toolspace = getToolSpace();
+ Point folderSize = getSize();
+ boolean visible = (toolspace.width == 0 || x < toolspace.x) && x + size.x < folderSize.x - borderRight;
+ closeBar.setVisible(visible);
+ }
+ }
+}
+private boolean setItemLocation() {
+ if (items.length == 0) return false;
+ Rectangle area = super.getClientArea();
+ int x = area.x;
+ int y = area.y + borderTop;
+ if (onBottom) y = Math.max(0, area.y + area.height - borderBottom - tabHeight);
+
+ boolean changed = false;
+ for (int i = topTabIndex - 1; i>=0; i--) {
+ // if the first visible tab is not the first tab
+ CTabItem tab = items[i];
+ x -= tab.width;
+ if (!changed && (tab.x != x || tab.y != y) ) changed = true;
+ // layout tab items from right to left thus making them invisible
+ tab.x = x;
+ tab.y = y;
+ }
+
+ x = area.x + borderLeft;
+ for (int i = topTabIndex; i < items.length; i++) {
+ // continue laying out remaining, visible items left to right
+ CTabItem tab = items[i];
+ tab.x = x;
+ tab.y = y;
+ x = x + tab.width;
+ }
+ setButtonBounds();
+ return changed;
+}
+private void setLastItem(int index) {
+ if (index < 0 || index > items.length - 1) return;
+ Rectangle area = getClientArea();
+ if (area.width <= 0) return;
+ int maxWidth = area.width;
+ Rectangle toolspace = getToolSpace();
+ if (toolspace.width > 0){
+ maxWidth -= toolspace.width;
+ }
+ int tabWidth = items[index].width;
+ while (index > 0) {
+ tabWidth += items[index - 1].width;
+ if (tabWidth > maxWidth) break;
+ index--;
+ }
+ topTabIndex = index;
+ setItemLocation();
+ redrawTabArea(-1);
+}
+/**
+ * Layout the items and store the client area size.
+ */
+boolean setItemBounds() {
+ boolean changed = false;
+ if (isDisposed()) return changed;
+ Rectangle area = super.getClientArea();
+
+ xClient = area.x + borderLeft + marginWidth;
+ if (onBottom) {
+ yClient = area.y + borderTop + marginHeight;
+ } else {
+ yClient = area.y + borderTop + tabHeight + 1 + marginHeight;
+ // +1 is for the line at the bottom of the tabs
+ }
+
+ if (area.width <= 0 || area.height <= 0 || items.length == 0) return changed;
+
+ int[] widths = new int[items.length];
+ GC gc = new GC(this);
+ for (int i = 0; i < items.length; i++) {
+ widths[i] = items[i].preferredWidth(gc);
+ }
+ gc.dispose();
+
+ int oldAverageWidth = 0;
+ int averageWidth = (area.width - borderLeft - borderRight) / items.length;
+ while (averageWidth > oldAverageWidth) {
+ int width = area.width - borderLeft - borderRight;
+ int count = items.length;
+ for (int i = 0; i < items.length; i++) {
+ if (widths[i] < averageWidth) {
+ width -= widths[i];
+ count--;
+ }
+ }
+ oldAverageWidth = averageWidth;
+ if (count > 0) {
+ averageWidth = width / count;
+ }
+ }
+ averageWidth = Math.max(averageWidth, MIN_TAB_WIDTH * tabHeight);
+ for (int i = 0; i < items.length; i++) {
+ if (widths[i] > averageWidth) {
+ widths[i] = averageWidth;
+ }
+ }
+
+ int totalWidth = 0;
+ for (int i = 0; i < items.length; i++) {
+ CTabItem tab = items[i];
+ if (tab.height != tabHeight || tab.width != widths[i]) changed = true;
+ tab.height = tabHeight;
+ tab.width = widths[i];
+ totalWidth += widths[i];
+ }
+
+ int areaWidth = area.x + area.width - borderRight;
+ if (totalWidth <= areaWidth) {
+ topTabIndex = 0;
+ }
+ if (setItemLocation()) changed = true;
+
+ // Is there a gap after last item showing
+ if (correctLastItem()) changed = true;
+ return changed;
+}
+private boolean onMnemonic (Event event) {
+ char key = event.character;
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] != null) {
+ char mnemonic = getMnemonic (items[i].getText ());
+ if (mnemonic != '\0') {
+ if (Character.toUpperCase (key) == Character.toUpperCase (mnemonic)) {
+ setSelection(i, true);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+/**
+ * Paint the receiver.
+ */
+private void onPaint(Event event) {
+ Font font = getFont();
+ if (oldFont == null || !oldFont.equals(font)) {
+ oldFont = font;
+ resetTabSize(true);
+ }
+ GC gc = event.gc;
+ Rectangle rect = super.getClientArea();
+ if (items.length == 0) {
+ if (showBorders) {
+ if ((getStyle() & SWT.FLAT) != 0) {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 1, rect.y + rect.height - 1);
+ } else {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 3, rect.y + rect.height - 3);
+
+ // fill in right and bottom edges with parent's background
+ gc.setBackground(getParent().getBackground());
+ gc.fillRectangle(rect.x + rect.width - 2, rect.y, 2, rect.height);
+ gc.fillRectangle(rect.x, rect.y + rect.height - 2, rect.width, 2);
+ }
+ gc.setForeground(getForeground());
+ }
+ return;
+ }
+
+ // redraw the Border
+ drawBorder(gc);
+
+ rect.x += borderLeft;
+ rect.y += borderTop;
+ rect.width -= borderLeft + borderRight;
+ rect.height -= borderTop + borderBottom;
+ Rectangle clip = gc.getClipping ();
+ gc.setClipping(clip.intersection(rect));
+
+ // Draw the unselected tabs first.
+ for (int i=0; i < items.length; i++) {
+ if (i != selectedIndex && event.getBounds().intersects(items[i].getBounds())) {
+ items[i].onPaint(gc, false);
+ }
+ }
+ // Selected tab comes last
+ if (selectedIndex != -1) {
+ items[selectedIndex].onPaint(gc, true);
+ }
+
+ // draw insertion mark
+ if (insertionIndex > -2) {
+ gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION));
+ if (insertionIndex == -1) {
+ Rectangle bounds = items[0].getBounds();
+ gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height - 1);
+ gc.drawLine(bounds.x - 2, bounds.y, bounds.x + 2, bounds.y);
+ gc.drawLine(bounds.x - 1, bounds.y + 1, bounds.x + 1, bounds.y + 1);
+ gc.drawLine(bounds.x - 1, bounds.y + bounds.height - 2, bounds.x + 1, bounds.y + bounds.height - 2);
+ gc.drawLine(bounds.x - 2, bounds.y + bounds.height - 1, bounds.x + 2, bounds.y + bounds.height - 1);
+
+ } else {
+ Rectangle bounds = items[insertionIndex].getBounds();
+ gc.drawLine(bounds.x + bounds.width, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height - 1);
+ gc.drawLine(bounds.x + bounds.width - 2, bounds.y, bounds.x + bounds.width + 2, bounds.y);
+ gc.drawLine(bounds.x + bounds.width - 1, bounds.y + 1, bounds.x + bounds.width + 1, bounds.y + 1);
+ gc.drawLine(bounds.x + bounds.width - 1, bounds.y + bounds.height - 2, bounds.x + bounds.width + 1, bounds.y + bounds.height - 2);
+ gc.drawLine(bounds.x + bounds.width - 2, bounds.y + bounds.height - 1, bounds.x + bounds.width + 2, bounds.y + bounds.height - 1);
+ }
+ }
+
+ gc.setForeground(getForeground());
+ gc.setBackground(getBackground());
+}
+private void redrawTabArea(int index) {
+ int x = 0, y = 0, width = 0, height = 0;
+ if (index == -1) {
+ Rectangle area = super.getClientArea();
+ if (area.width == 0 || area.height == 0) return;
+ width = area.x + area.width - borderLeft - borderRight;
+ height = tabHeight + 1; // +1 causes top line between content and tabs to be redrawn
+ x = area.x + borderLeft;
+ y = area.y + borderTop;
+ if (onBottom) {
+ y = Math.max(0, area.y + area.height - borderBottom - height);
+ }
+ } else {
+ CTabItem item = items[index];
+ x = item.x;
+ y = item.y;
+ Rectangle area = super.getClientArea();
+ width = area.x + area.width - x;
+ height = item.height;
+ }
+ redraw(x, y, width, height, false);
+}
+
+/**
+ * Removes the listener.
+ *
+ * @param listener the listener
+ *
+ * @exception SWTError
+ * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
+ */
+public void removeSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ removeListener(SWT.Selection, listener);
+ removeListener(SWT.DefaultSelection, listener);
+}
+/**
+ * Removes the listener.
+ *
+ * @param listener the listener
+ *
+ * @exception SWTError
+ * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
+ */
+public void removeCTabFolderListener(CTabFolderListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ if (tabListeners.length == 0) return;
+ int index = -1;
+ for (int i = 0; i < tabListeners.length; i++) {
+ if (listener == tabListeners[i]){
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) return;
+ if (tabListeners.length == 1) {
+ tabListeners = new CTabFolderListener[0];
+ showClose = false;
+ setButtonBounds();
+ return;
+ }
+ CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
+ System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
+ System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
+ tabListeners = newTabListeners;
+}
+
+/**
+ * The widget was resized. Adjust the size of the currently selected page.
+ */
+private void onResize() {
+
+ if (items.length == 0) {
+ redraw();
+ return;
+ }
+
+ if (setItemBounds()) {
+ redrawTabArea(-1);
+ }
+
+ Point size = getSize();
+ if (oldSize == null) {
+ redraw();
+ } else {
+ if (onBottom && size.y != oldSize.y) {
+ redraw();
+ } else {
+ int x1 = Math.min(size.x, oldSize.x);
+ if (size.x != oldSize.x) x1 -= 10;
+ int y1 = Math.min(size.y, oldSize.y);
+ if (size.y != oldSize.y) y1 -= 10;
+ int x2 = Math.max(size.x, oldSize.x);
+ int y2 = Math.max(size.y, oldSize.y);
+ redraw(0, y1, x2 + 10, y2 - y1, false);
+ redraw(x1, 0, x2 - x1, y2, false);
+ }
+ }
+ oldSize = size;
+
+ // resize content
+ if (selectedIndex != -1) {
+ Control control = items[selectedIndex].getControl();
+ if (control != null && !control.isDisposed()) {
+ control.setBounds(getClientArea());
+ }
+ }
+}
+
+public void setBackground (Color color) {
+ super.setBackground(color);
+ background = color;
+ // init inactive close button
+ inactiveCloseBar.setBackground(color);
+
+ // init scroll buttons
+ arrowBar.setBackground(color);
+
+ //init topRight control
+ if (topRight != null)
+ topRight.setBackground(color);
+
+ // init close button
+ if (gradientColors == null) {
+ closeBar.setBackground(color);
+ }
+}
+/**
+ * Specify a gradient of colours to be draw in the background of the selected tab.
+ * For example to draw a gradient that varies from dark blue to blue and then to
+ * white, use the following call to setBackground:
+ * <pre>
+ * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
+ * display.getSystemColor(SWT.COLOR_BLUE),
+ * display.getSystemColor(SWT.COLOR_WHITE),
+ * display.getSystemColor(SWT.COLOR_WHITE)},
+ * new int[] {25, 50, 100});
+ * </pre>
+ *
+ * @param colors an array of Color that specifies the colors to appear in the gradient
+ * in order of appearance left to right. The value <code>null</code> clears the
+ * background gradient. The value <code>null</code> can be used inside the array of
+ * Color to specify the background color.
+ * @param percents an array of integers between 0 and 100 specifying the percent of the width
+ * of the widget at which the color should change. The size of the percents array must be one
+ * less than the size of the colors array.
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * </ul>
+ */
+
+public void setSelectionBackground(Color[] colors, int[] percents) {
+ checkWidget();
+ if (colors != null) {
+ if (percents == null || percents.length != colors.length - 1) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (getDisplay().getDepth() < 15) {
+ // Don't use gradients on low color displays
+ colors = new Color[] { colors[0] };
+ percents = new int[] { };
+ }
+ for (int i = 0; i < percents.length; i++) {
+ if (percents[i] < 0 || percents[i] > 100) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (i > 0 && percents[i] < percents[i-1]) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ }
+ }
+
+ // Are these settings the same as before?
+ if (backgroundImage == null) {
+ if ((gradientColors != null) && (colors != null) &&
+ (gradientColors.length == colors.length)) {
+ boolean same = false;
+ for (int i = 0; i < gradientColors.length; i++) {
+ if (gradientColors[i] == null) {
+ same = colors[i] == null;
+ } else {
+ same = gradientColors[i].equals(colors[i]);
+ }
+ if (!same) break;
+ }
+ if (same) {
+ for (int i = 0; i < gradientPercents.length; i++) {
+ same = gradientPercents[i] == percents[i];
+ if (!same) break;
+ }
+ }
+ if (same) return;
+ }
+ } else {
+ backgroundImage = null;
+ }
+ // Store the new settings
+ if (colors == null) {
+ gradientColors = null;
+ gradientPercents = null;
+ closeBar.setBackground(background);
+ } else {
+ gradientColors = new Color[colors.length];
+ for (int i = 0; i < colors.length; ++i)
+ gradientColors[i] = colors[i];
+ gradientPercents = new int[percents.length];
+ for (int i = 0; i < percents.length; ++i)
+ gradientPercents[i] = percents[i];
+ if (getDisplay().getDepth() < 15) closeBar.setBackground(background);
+ else closeBar.setBackground(gradientColors[gradientColors.length - 1]);
+ }
+
+ // Refresh with the new settings
+ if (selectedIndex > -1) redrawTabArea(selectedIndex);
+}
+
+/**
+ * Set the image to be drawn in the background of the selected tab.
+ *
+ * @param image the image to be drawn in the background
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelectionBackground(Image image) {
+ checkWidget();
+ if (image == backgroundImage) return;
+ if (image != null) {
+ gradientColors = null;
+ gradientPercents = null;
+ }
+ backgroundImage = image;
+ redrawTabArea(selectedIndex);
+}
+/**
+ * Toggle the visibility of the border
+ *
+ * @param show true if the border should be displayed
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBorderVisible(boolean show) {
+ checkWidget();
+ if (showBorders == show) return;
+
+ showBorders = show;
+ if (showBorders) {
+ if ((getStyle() & SWT.FLAT) != 0) {
+ borderBottom = borderTop = borderLeft = borderRight = 1;
+ } else {
+ borderLeft = borderTop = 1;
+ borderRight = borderBottom = 3;
+ }
+ } else {
+ borderBottom = borderTop = borderLeft = borderRight = 0;
+ }
+ oldSize = null;
+ notifyListeners(SWT.Resize, new Event());
+}
+public void setFont(Font font) {
+ checkWidget();
+ if (font != null && font.equals(getFont())) return;
+ super.setFont(font);
+ oldFont = getFont();
+ resetTabSize(true);
+}
+/**
+ * Set the foreground color of the selected tab.
+ *
+ * @param color the color of the text displayed in the selected tab
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelectionForeground (Color color) {
+ checkWidget();
+ if (selectionForeground == color) return;
+ if (color == null) color = getForeground();
+ selectionForeground = color;
+ if (selectedIndex > -1) {
+ redrawTabArea(selectedIndex);
+ }
+}
+/**
+ * Display an insert marker before or after the specified tab item.
+ * Null will clear the mark.
+ *
+ * @param item the item with which the mark is associated or null
+ *
+ * @param after true if the mark should be displayed after the specified item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setInsertMark(CTabItem item, boolean after) {
+ checkWidget();
+ int index = -1;
+ if (item != null) {
+ index = indexOf(item);
+ }
+ setInsertMark(index, after);
+}
+/**
+ * Display an insert marker before or after the specified tab item.
+ * -1 will clear the mark.
+ *
+ * @param item the index of the item with which the mark is associated or null
+ *
+ * @param after true if the mark should be displayed after the specified item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setInsertMark(int index, boolean after) {
+ checkWidget();
+ if (index < -1 || index >= getItemCount()) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+
+ if (index == -1) {
+ index = -2;
+ } else {
+ index = after ? index : --index;
+ }
+
+ if (insertionIndex == index) return;
+ int oldIndex = insertionIndex;
+ insertionIndex = index;
+ if (index > -1) redrawTabArea(index);
+ if (oldIndex > 1) redrawTabArea(oldIndex);
+}
+
+/**
+ * Set the selection to the tab at the specified index.
+ *
+ * @param index the index of the tab item to be selected
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection(int index) {
+ checkWidget();
+ if (index < 0 || index >= items.length) return;
+ if (selectedIndex == index) return;
+
+ int oldIndex = selectedIndex;
+ selectedIndex = index;
+
+ Control control = items[index].control;
+ if (control != null && !control.isDisposed()) {
+ control.setBounds(getClientArea());
+ control.setVisible(true);
+ }
+
+ if (oldIndex != -1) {
+ control = items[oldIndex].control;
+ if (control != null && !control.isDisposed()) {
+ control.setVisible(false);
+ }
+ }
+ showItem(items[selectedIndex]);
+ setButtonBounds();
+ redrawTabArea(-1);
+}
+/**
+ * Set the control that appears in the top right corner of the tab folder.
+ * Typically this is a close button or a composite with a Menu and close button.
+ * The topRight control is optional. Setting the top right control to null will remove it from the tab folder.
+ *
+ * @since 2.1
+ *
+ * @param control the control to be displayed in the top right corner or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
+ * </ul>
+ */
+public void setTopRight(Control control) {
+ checkWidget();
+ if (control != null && control.getParent() != this) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ topRight = control;
+ resetTabSize(true);
+}
+
+/**
+ * Shows the item. If the item is already showing in the receiver,
+ * this method simply returns. Otherwise, the items are scrolled until
+ * the item is visible.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see CTabFolder#showSelection()
+ *
+ * @since 2.0
+ */
+public void showItem (CTabItem item) {
+ checkWidget();
+ if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+
+ int index = indexOf(item);
+ if (index < topTabIndex) {
+ topTabIndex = index;
+ setItemLocation();
+ redrawTabArea(-1);
+ return;
+ }
+ Rectangle area = getClientArea();
+ if (area.width <= 0) {
+ topTabIndex = index;
+ return;
+ }
+ int rightEdge = area.x + area.width;
+ Rectangle rect = getToolSpace();
+ if (rect.width > 0) {
+ rightEdge -= rect.width;
+ }
+ if (item.x + item.width < rightEdge) return;
+ setLastItem(index);
+}
+/**
+ * Shows the selection. If the selection is already showing in the receiver,
+ * this method simply returns. Otherwise, the items are scrolled until
+ * the selection is visible.
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see CTabFolder#showItem(CTabItem)
+ *
+ * @since 2.0
+ *
+ */
+public void showSelection () {
+ checkWidget ();
+ if (selectedIndex != -1) {
+ showItem(getSelection());
+ }
+}
+
+char getMnemonic (String string) {
+ int index = 0;
+ int length = string.length ();
+ do {
+ while ((index < length) && (string.charAt (index) != '&')) index++;
+ if (++index >= length) return '\0';
+ if (string.charAt (index) != '&') return string.charAt (index);
+ index++;
+ } while (index < length);
+ return '\0';
+}
+/**
+ * Set the selection to the tab at the specified item.
+ *
+ * @param index the tab item to be selected
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_NULL_ARGUMENT - if argument is null</li>
+ * </ul>
+ */
+public void setSelection(CTabItem item) {
+ checkWidget();
+ if (item == null)
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ int index = indexOf(item);
+ setSelection(index);
+}
+/**
+ * Set the selection to the tab at the specified index.
+ */
+private void setSelection(int index, boolean notify) {
+ int oldSelectedIndex = selectedIndex;
+ setSelection(index);
+ if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
+ Event event = new Event();
+ event.item = getItem(selectedIndex);
+ notifyListeners(SWT.Selection, event);
+ }
+}
+
+private void updateCloseBar() {
+ int imageHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN - 4;
+ if (imageHeight < 8) return;
+
+ if (closeImage != null && closeImage.getBounds().height == imageHeight) return;
+
+ if (closeBar != null) closeBar.dispose();
+ closeBar = null;
+ if (inactiveCloseBar != null) inactiveCloseBar.dispose();
+ inactiveCloseBar = null;
+ createCloseBar();
+
+ ToolItem closeItem = closeBar.getItems()[0];
+ ToolItem inactiveCloseItem = inactiveCloseBar.getItems()[0];
+
+ if (closeImage != null) closeImage.dispose();
+
+ Display display = getDisplay();
+ Color foreground = getForeground();
+ Color black = display.getSystemColor(SWT.COLOR_BLACK);
+ Color background = getBackground();
+
+ PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()});
+ ImageData imageData = new ImageData(imageHeight, imageHeight, 4, palette);
+ imageData.transparentPixel = 1;
+ closeImage = new Image(display, imageData);
+ GC gc = new GC(closeImage);
+ gc.setBackground(background);
+ gc.fillRectangle(0, 0, imageHeight, imageHeight);
+ gc.setForeground(black);
+
+ //draw an 8x7 'x' centered in image
+ int h = (imageHeight / 2 )* 2;
+ int inset = (h - 8) / 2;
+ gc.drawLine( inset, inset, h - inset - 1, h - inset - 1);
+ gc.drawLine( inset + 1, inset, h - inset, h - inset - 1);
+ gc.drawLine( inset, h - inset - 1, h - inset - 1, inset);
+ gc.drawLine( inset + 1, h - inset - 1, h - inset, inset);
+
+ gc.dispose();
+
+ closeItem.setImage(closeImage);
+ inactiveCloseItem.setImage(closeImage);
+}
+private void updateArrowBar() {
+
+ int imageHeight = tabHeight - 6;
+ if (imageHeight < 10) return;
+
+ if (arrowLeftImage != null && arrowLeftImage.getBounds().height == imageHeight) return;
+
+ if (arrowBar != null) arrowBar.dispose();
+ arrowBar = null;
+ if (arrowLeftImage != null) arrowLeftImage.dispose();
+ if (arrowRightImage != null) arrowRightImage.dispose();
+
+ createArrowBar();
+ ToolItem[] items = arrowBar.getItems();
+ ToolItem left = items[0];
+ ToolItem right = items[1];
+
+ Display display = getDisplay();
+ Color foreground = getForeground();
+ Color black = display.getSystemColor(SWT.COLOR_BLACK);
+ Color background = getBackground();
+
+ PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()});
+ ImageData imageData = new ImageData(7, imageHeight, 4, palette);
+ imageData.transparentPixel = 1;
+ arrowLeftImage = new Image(display, imageData);
+ GC gc = new GC(arrowLeftImage);
+ gc.setBackground(background);
+ gc.fillRectangle(0, 0, 7, imageHeight);
+ gc.setBackground(black);
+ //draw a 10x5 '<' centered vertically in image
+ int h = (imageHeight / 2 )* 2;
+ int midpoint = h / 2 - 1;
+ int[] pointArr = new int[] {6, midpoint - 5,
+ 1, midpoint,
+ 6, midpoint + 5,};
+ gc.fillPolygon(pointArr);
+ gc.dispose();
+
+ palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()});
+ imageData = new ImageData(7, imageHeight, 4, palette);
+ imageData.transparentPixel = 1;
+ arrowRightImage = new Image(display, imageData);
+ gc = new GC(arrowRightImage);
+ gc.setBackground(background);
+ gc.fillRectangle(0, 0, 7, imageHeight);
+ gc.setBackground(black);
+ //draw a 10x5 '>' centered vertically in image
+ pointArr = new int[] {1, midpoint - 5,
+ 6, midpoint,
+ 1, midpoint + 5,};
+ gc.fillPolygon(pointArr);
+ gc.dispose();
+
+ left.setImage(arrowLeftImage);
+ right.setImage(arrowRightImage);
+}
+
+private void onMouseDoubleClick(Event event) {
+ Event e = new Event();
+ e.item = getItem(new Point(event.x, event.y));
+ notifyListeners(SWT.DefaultSelection, e);
+}
+/**
+ * A mouse button was pressed down.
+ * If a tab was hit select the tab.
+ */
+private void onMouseDown(Event event) {
+ for (int i=0; i<items.length; i++) {
+ if (items[i].getBounds().contains(new Point(event.x, event.y))) {
+ if (i == selectedIndex) {
+ showSelection();
+ return;
+ }
+ forceFocus();
+ setSelection(i, true);
+ if (isFocusControl()) setFocus();
+ return;
+ }
+ }
+}
+
+private void onMouseExit(Event event) {
+ Rectangle inactiveBounds = inactiveCloseBar.getBounds();
+ if (inactiveBounds.contains(event.x, event.y)) return;
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+
+ showToolTip = false;
+ toolTipItem = null;
+ if (tip != null && !tip.isDisposed() && tip.isVisible()) tip.setVisible(false);
+}
+
+private void onMouseHover(Event event) {
+ if (tip == null || tip.isDisposed()) return;
+ showToolTip = true;
+ showToolTip(event.x, event.y);
+}
+private void showToolTip (int x, int y) {
+ CTabItem item = getItem(new Point (x, y));
+ if (item != null) {
+ if (item == toolTipItem) return;
+ toolTipItem = item;
+ String tooltip = item.getToolTipText();
+ if (tooltip != null) {
+ Display display = tip.getDisplay();
+ label.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
+ label.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
+ label.setText(tooltip);
+ Point labelSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ labelSize.x += 2; labelSize.y += 2;
+ label.setSize(labelSize);
+ tip.pack();
+ /*
+ * On some platforms, there is a minimum size for a shell
+ * which may be greater than the label size.
+ * To avoid having the background of the tip shell showing
+ * around the label, force the label to fill the entire client area.
+ */
+ Rectangle area = tip.getClientArea();
+ label.setSize(area.width, area.height);
+ /*
+ * Position the tooltip and ensure that it is not located off
+ * the screen.
+ */
+ Point pt = new Point(item.x + item.width / 4, item.y + item.height + 2);
+ pt = toDisplay(pt);
+ Rectangle rect = display.getBounds();
+ Point tipSize = tip.getSize();
+ pt.x = Math.max (0, Math.min (pt.x, rect.width - tipSize.x));
+ pt.y = Math.max (0, Math.min (pt.y, rect.height - tipSize.y));
+ tip.setLocation(pt);
+ tip.setVisible(true);
+ return;
+ }
+ }
+
+ toolTipItem = null;
+ if (tip != null && !tip.isDisposed() && tip.isVisible()) tip.setVisible(false);
+}
+private void onMouseMove(Event event) {
+ if (showToolTip) {
+ showToolTip(event.x, event.y);
+ }
+
+ if (!showClose) return;
+
+ CTabItem item = null;
+ for (int i=0; i<items.length; i++) {
+ Rectangle rect = items[i].getBounds();
+ if (rect.contains(new Point(event.x, event.y))) {
+ item = items[i];
+ break;
+ }
+ }
+ if (item == inactiveItem) return;
+
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+
+ if (item == null || item == getSelection()) return;
+
+ int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
+ Point size = inactiveCloseBar.computeSize(SWT.DEFAULT, toolbarHeight);
+ int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
+ int y = item.y + Math.max(0, (item.height - toolbarHeight)/2);
+ Rectangle toolspace = getToolSpace();
+ Point folderSize = getSize();
+ if ((toolspace.width == 0 || x < toolspace.x) && x + size.x < folderSize.x - borderRight) {
+ inactiveCloseBar.setBounds(x, y, size.x, toolbarHeight);
+ inactiveCloseBar.setVisible(true);
+ inactiveItem = item;
+ }
+}
+private void onTraverse (Event event) {
+ switch (event.detail) {
+ case SWT.TRAVERSE_ESCAPE:
+// TEMPORARY CODE See bug report 17372
+// case SWT.TRAVERSE_RETURN:
+ case SWT.TRAVERSE_TAB_NEXT:
+ case SWT.TRAVERSE_TAB_PREVIOUS:
+ event.doit = true;
+ break;
+ case SWT.TRAVERSE_MNEMONIC:
+ event.doit = onMnemonic(event);
+ if (event.doit) event.detail = SWT.TRAVERSE_NONE;
+ break;
+ case SWT.TRAVERSE_PAGE_NEXT:
+ case SWT.TRAVERSE_PAGE_PREVIOUS:
+ event.doit = onPageTraversal(event);
+ if (event.doit) event.detail = SWT.TRAVERSE_NONE;
+ break;
+ }
+}
+
+private boolean onPageTraversal(Event event) {
+ int count = getItemCount ();
+ if (count == 0) return false;
+ int index = getSelectionIndex ();
+ if (index == -1) {
+ index = 0;
+ } else {
+ int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
+ index = (index + offset + count) % count;
+ }
+ setSelection (index, true);
+ return true;
+}
+
+/**
+ * Answer true if not all tabs can be visible in the receive
+ * thus requiring the scroll buttons to be visible.
+ */
+private boolean scroll_leftVisible() {
+ return topTabIndex > 0;
+}
+
+/**
+ * Answer true if not all tabs can be visible in the receive
+ * thus requiring the scroll buttons to be visible.
+ */
+private boolean scroll_rightVisible() {
+ // only show Scroll buttons if there is more than one item
+ // and if we are not already at the last item
+ if (items.length < 2) return false;
+ Rectangle area = getClientArea();
+ int rightEdge = area.x + area.width;
+ if (rightEdge <= 0) return false;
+ if (topTabIndex > 0) {
+ rightEdge -= arrowBar.getSize().x;
+ }
+ if (topRight != null) {
+ rightEdge -= topRight.getSize().x;
+ }
+ CTabItem item = items[items.length-1];
+ return (item.x + item.width > rightEdge);
+}
+
+/**
+ * Scroll the tab items to the left.
+ */
+private void scroll_scrollLeft() {
+ if (items.length == 0) return;
+ setLastItem(topTabIndex - 1);
+}
+
+/**
+ * Scroll the tab items to the right.
+ */
+private void scroll_scrollRight() {
+ int lastIndex = getLastItem();
+ topTabIndex = lastIndex + 1;
+ setItemLocation();
+ correctLastItem();
+ redrawTabArea(-1);
+}
+private boolean correctLastItem() {
+ Rectangle area = getClientArea();
+ int rightEdge = area.x + area.width;
+ if (rightEdge <= 0) return false;
+ Rectangle toolspace = getToolSpace();
+ if (toolspace.width > 0) {
+ rightEdge -= toolspace.width;
+ }
+ CTabItem item = items[items.length - 1];
+ if (item.x + item.width < rightEdge) {
+ setLastItem(items.length - 1);
+ return true;
+ }
+ return false;
+}
+/**
+ * Specify a fixed height for the tab items. If no height is specified,
+ * the default height is the height of the text or the image, whichever
+ * is greater. Specifying a height of 0 will revert to the default height.
+ *
+ * @param height the pixel value of the height or 0
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
+ * </ul>
+ */
+public void setTabHeight(int height) {
+ checkWidget();
+ if (height < 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ fixedTabHeight = true;
+ if (tabHeight == height) return;
+ tabHeight = height;
+ oldSize = null;
+ notifyListeners(SWT.Resize, new Event());
+}
+void resetTabSize(boolean checkHeight){
+ int oldHeight = tabHeight;
+ if (!fixedTabHeight && checkHeight) {
+ int tempHeight = 0;
+ GC gc = new GC(this);
+ for (int i=0; i < items.length; i++) {
+ tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc));
+ }
+ gc.dispose();
+ if (topRight != null) tempHeight = Math.max(tempHeight, topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
+ tabHeight = tempHeight;
+ }
+
+ if (tabHeight != oldHeight){
+ oldSize = null;
+ notifyListeners(SWT.Resize, new Event());
+ } else {
+ setItemBounds();
+ redraw();
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderAdapter.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderAdapter.java
index 0bb5acae8f..d3324b4523 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderAdapter.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderAdapter.java
@@ -1,12 +1,12 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-public class CTabFolderAdapter implements CTabFolderListener {
- public void itemClosed(CTabFolderEvent event){};
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+public class CTabFolderAdapter implements CTabFolderListener {
+ public void itemClosed(CTabFolderEvent event){};
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderEvent.java
index 3cc4757e41..d36dd58508 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderEvent.java
@@ -1,20 +1,20 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.TypedEvent;
-import org.eclipse.swt.widgets.*;
-
-public class CTabFolderEvent extends TypedEvent {
- public Widget item;
- public boolean doit;
-
-CTabFolderEvent(Widget w) {
- super(w);
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.TypedEvent;
+import org.eclipse.swt.widgets.*;
+
+public class CTabFolderEvent extends TypedEvent {
+ public Widget item;
+ public boolean doit;
+
+CTabFolderEvent(Widget w) {
+ super(w);
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderListener.java
index bb1b33890b..fd19b332cc 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderListener.java
@@ -1,12 +1,12 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-public interface CTabFolderListener {
- public void itemClosed(CTabFolderEvent event);
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+public interface CTabFolderListener {
+ public void itemClosed(CTabFolderEvent event);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java
index d2136a3625..ded51fcee7 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java
@@ -1,518 +1,518 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-public class CTabItem extends Item {
- CTabFolder parent;
- int x,y,width,height = 0;
- String toolTipText;
- Control control; // the tab page
-
- private Image disabledImage;
-
- // internal constants
- static final int LEFT_MARGIN = 4;
- static final int RIGHT_MARGIN = 4;
- static final int TOP_MARGIN = 3;
- static final int BOTTOM_MARGIN = 3;
- private static final int INTERNAL_SPACING = 2;
-
- private static final String ellipsis = "..."; //$NON-NLS-1$
-
- String shortenedText;
- int shortenedTextWidth;
-
-/**
- * Constructs a new instance of this class given its parent
- * (which must be a <code>CTabFolder</code>) and a style value
- * describing its behavior and appearance. The item is added
- * to the end of the items maintained by its parent.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT
- * @see Widget#getStyle
- */
-public CTabItem (CTabFolder parent, int style) {
- this(parent, style, parent.getItemCount());
-}
-/**
- * Constructs a new instance of this class given its parent
- * (which must be a <code>CTabFolder</code>), a style value
- * describing its behavior and appearance, and the index
- * at which to place it in the items maintained by its parent.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- * @param index the index to store the receiver in its parent
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT
- * @see Widget#getStyle
- */
-public CTabItem (CTabFolder parent, int style, int index) {
- super (parent, checkStyle(style));
- parent.createItem (this, index);
-}
-private static int checkStyle(int style) {
- return SWT.NONE;
-}
-
-public void dispose () {
- if (isDisposed()) return;
- parent.destroyItem(this);
- super.dispose();
- parent = null;
- control = null;
- toolTipText = null;
-}
-
-/**
- * Returns a rectangle describing the receiver's size and location
- * relative to its parent.
- *
- * @param index the index that specifies the column
- * @return the receiver's bounding column rectangle
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public Rectangle getBounds () {
- //checkWidget();
- return new Rectangle(x, y, width, height);
-}
-/**
-* Gets the control that is displayed in the content are of the tab item.
-*
-* @return the control
-*
-* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
-* when called from the wrong thread
-* @exception SWTError(ERROR_WIDGET_DISPOSED)
-* when the widget has been disposed
-*/
-public Control getControl () {
- checkWidget();
- return control;
-}
-public Display getDisplay() {
- if (parent == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED);
- return parent.getDisplay();
-}
-/**
- * Get the image displayed in the tab if the tab is disabled.
- *
- * @return the disabled image or null
- */
-public Image getDisabledImage(){
- //checkWidget();
- return disabledImage;
-}
-/**
- * Returns the receiver's parent, which must be a <code>CTabFolder</code>.
- *
- * @return the receiver's parent
- */
-public CTabFolder getParent () {
- //checkWidget();
- return parent;
-}
-/**
- * Returns the receiver's tool tip text, or null if it has
- * not been set.
- *
- * @return the receiver's tool tip text
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public String getToolTipText () {
- checkWidget();
- return toolTipText;
-}
-/**
- * Paint the receiver.
- */
-void onPaint(GC gc, boolean isSelected) {
-
- if (width == 0 || height == 0) return;
-
- Display display = getDisplay();
- Color highlightShadow = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
- Color normalShadow = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
-
- int index = parent.indexOf(this);
-
- if (isSelected) {
-
- Rectangle bounds = null;
- if (!parent.onBottom) {
- if (index == parent.topTabIndex) {
- bounds = new Rectangle(x + 1, y + 1, width - 2, height - 1);
- } else {
- bounds = new Rectangle(x + 2, y + 1, width - 3, height - 1);
- }
- } else {
- if (index == parent.topTabIndex) {
- bounds = new Rectangle(x + 1, y + 1, width - 2, height - 2);
- } else {
- bounds = new Rectangle(x + 2, y + 1, width - 3, height - 2);
- }
- }
- if (parent.backgroundImage != null) {
- // draw a background image behind the text
- Rectangle imageRect = parent.backgroundImage.getBounds();
- gc.drawImage(parent.backgroundImage, 0, 0, imageRect.width, imageRect.height,
- bounds.x, bounds.y, bounds.width, bounds.height);
- } else if (parent.gradientColors != null) {
- // draw a gradient behind the text
- Color oldBackground = gc.getBackground();
- if (parent.gradientColors.length == 1) {
- if (parent.gradientColors[0] != null) gc.setBackground(parent.gradientColors[0]);
- gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
- } else {
- Color oldForeground = gc.getForeground();
- Color lastColor = parent.gradientColors[0];
- if (lastColor == null) lastColor = oldBackground;
- for (int i = 0, pos = 0; i < parent.gradientPercents.length; ++i) {
- gc.setForeground(lastColor);
- lastColor = parent.gradientColors[i + 1];
- if (lastColor == null) lastColor = oldBackground;
- gc.setBackground(lastColor);
- int gradientWidth = (parent.gradientPercents[i] * bounds.width / 100) - pos;
- gc.fillGradientRectangle(bounds.x + pos, bounds.y, gradientWidth, bounds.height, false);
- pos += gradientWidth;
- }
- gc.setForeground(oldForeground);
- }
- gc.setBackground(oldBackground);
- }
-
- // draw tab lines
- if (!parent.onBottom) {
- gc.setForeground(normalShadow);
- if (index != parent.topTabIndex) {
- gc.drawLine(x + 1, y, x + 1, y);
- gc.drawLine(x, y + 1, x, y + height - 2);
- gc.drawLine(x, y + height - 1, x, y + height - 1);
- }
- gc.drawLine(x + width - 1, y, x + width - 1, y);
- gc.drawLine(x + width, y + 1, x + width, y + height - 2);
- gc.drawLine(x + width, y + height - 1, x + width, y + height - 1);
-
- gc.setForeground(highlightShadow);
- if (index != parent.topTabIndex) {
- gc.drawLine(x + 2, y, x + 2, y);
- gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
- gc.drawLine(x + 1, y + height - 1, x + 1, y + height - 1);
- } else {
- gc.drawLine(x, y, x, y + height - 1);
- }
-
- gc.drawLine(x + width - 2, y, x + width - 2, y);
- gc.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 2);
- gc.drawLine(x + width - 1, y + height - 1, x + width - 1, y + height - 1);
-
- // light line across top
- if (index != parent.topTabIndex) {
- gc.drawLine(x + 3, y, x + width - 3, y);
- } else {
- gc.drawLine(x + 1, y, x + width - 3, y);
- }
- } else {
- gc.setForeground(normalShadow);
- if (index != parent.topTabIndex) {
- gc.drawLine(x, y, x, y);
- gc.drawLine(x, y + 1, x, y + height - 2);
- gc.drawLine(x + 1, y + height - 1, x + 1, y + height - 1);
- }
- gc.drawLine(x + width, y, x + width, y);
- gc.drawLine(x + width, y + 1, x + width, y + height - 2);
- gc.drawLine(x + width - 1, y + height - 1, x + width - 1, y + height - 1);
-
- gc.setForeground(highlightShadow);
- if (index != parent.topTabIndex) {
- gc.drawLine(x + 1, y, x + 1, y);
- gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
- gc.drawLine(x + 2, y + height - 1, x + 2, y + height - 1);
- } else {
- gc.drawLine(x, y, x, y + height - 1);
- }
-
- gc.drawLine(x + width - 1, y, x + width - 1, y);
- gc.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 2);
- gc.drawLine(x + width - 2, y + height - 1, x + width - 2, y + height - 1);
-
- // light line across top and bottom
- if (index != parent.topTabIndex) {
- gc.drawLine(x + 1, y, x + width - 2, y);
- gc.drawLine(x + 2, y + height - 1, x + width - 3, y + height - 1);
- } else {
- gc.drawLine(x + 1, y, x + width - 2, y);
- gc.drawLine(x + 1, y + height - 1, x + width - 3, y + height - 1);
- }
- }
- if (parent.isFocusControl()) {
- // draw a focus rectangle
- int x1, y1, width1, height1;
- if (!parent.onBottom) {
- if (index == parent.topTabIndex) {
- x1 = x + 1; y1 = y + 1; width1 = width - 2; height1 = height - 1;
- } else {
- x1 = x + 2; y1 = y + 1; width1 = width - 3; height1 = height - 1;
- }
- } else {
- if (index == parent.topTabIndex) {
- x1 = x + 1; y1 = y + 1; width1 = width - 2; height1 = height - 2;
- } else {
- x1 = x + 2; y1 = y + 1; width1 = width - 3; height1 = height - 2;
- }
- }
- gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
- gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
- gc.drawFocus(x1, y1, width1, height1);
- }
- } else {
- // draw tab lines for unselected items
- gc.setForeground(normalShadow);
- if (!parent.onBottom) {
- if (index != parent.topTabIndex && index != parent.getSelectionIndex() + 1) {
- gc.drawLine(x, y, x, y + (height / 2));
- }
- } else {
- if (index != parent.topTabIndex && index != parent.getSelectionIndex() + 1) {
- gc.drawLine(x, y + (height / 2), x, y + height - 1);
- }
- }
-
- }
-
- // draw Image
- int xDraw = x + LEFT_MARGIN;
-
- Image image = getImage();
- if (!isSelected && image != null) {
- Image temp = getDisabledImage();
- if (temp != null){
- image = temp;
- }
- }
- if (image != null) {
- Rectangle imageBounds = image.getBounds();
- int imageX = xDraw;
- int imageHeight = Math.min(height - BOTTOM_MARGIN - TOP_MARGIN, imageBounds.height);
- int imageY = y + (height - imageHeight) / 2;
- int imageWidth = imageBounds.width * imageHeight / imageBounds.height;
- gc.drawImage(image,
- imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height,
- imageX, imageY, imageWidth, imageHeight);
- xDraw += imageWidth + INTERNAL_SPACING;
- }
-
- // draw Text
- int textWidth = x + width - xDraw - RIGHT_MARGIN;
- if (isSelected && parent.showClose) {
- textWidth = x + width - xDraw - parent.closeBar.getSize().x - RIGHT_MARGIN;
- }
- if (shortenedText == null || shortenedTextWidth != textWidth) {
- shortenedText = shortenText(gc, getText(), textWidth);
- shortenedTextWidth = textWidth;
- }
- String text = shortenedText;
-
- if (isSelected && parent.selectionForeground != null) {
- gc.setForeground(parent.selectionForeground);
- } else {
- gc.setForeground(parent.getForeground());
- }
- int textY = y + (height - gc.textExtent(text, SWT.DRAW_MNEMONIC).y) / 2;
- gc.drawText(text, xDraw, textY, SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC);
-
- gc.setForeground(parent.getForeground());
-}
-private static String shortenText(GC gc, String text, int width) {
- if (gc.textExtent(text, SWT.DRAW_MNEMONIC).x <= width) return text;
-
- int ellipseWidth = gc.textExtent(ellipsis, SWT.DRAW_MNEMONIC).x;
- int length = text.length();
- int end = length - 1;
- while (end > 0) {
- text = text.substring(0, end);
- int l1 = gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
- if (l1 + ellipseWidth <= width) {
- return text + ellipsis;
- }
- end--;
- }
- return text + ellipsis;
-}
-/**
- * Answer the preferred height of the receiver for the GC.
- */
-int preferredHeight(GC gc) {
- Image image = getImage();
- int height = 0;
- if (image != null) height = image.getBounds().height;
- String text = getText();
- height = Math.max(height, gc.textExtent(text, SWT.DRAW_MNEMONIC).y);
- return height + TOP_MARGIN + BOTTOM_MARGIN;
-}
-/**
- * Answer the preferred width of the receiver for the GC.
- */
-int preferredWidth(GC gc) {
- int width = 0;
- Image image = getImage();
- if (image != null) width += image.getBounds().width;
- String text = getText();
- if (text != null) {
- if (image != null) width += INTERNAL_SPACING;
- width += gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
- }
- if (parent.showClose) width += INTERNAL_SPACING + preferredHeight(gc); // closebar will be square and will fill preferred height
- return width + LEFT_MARGIN + RIGHT_MARGIN;
-}
-/**
- * Sets the control that is used to fill the client area of
- * the tab folder when the user selects the tab item.
- * <p>
- * @param control the new control (or null)
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
- * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setControl (Control control) {
- checkWidget();
- if (control != null) {
- if (control.isDisposed()) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
- if (control.getParent() != parent) SWT.error (SWT.ERROR_INVALID_PARENT);
- }
- if (this.control != null && !this.control.isDisposed()) {
- this.control.setVisible(false);
- }
- this.control = control;
- if (this.control != null) {
- int index = parent.indexOf (this);
- if (index == parent.getSelectionIndex ()){
- this.control.setBounds(parent.getClientArea ());
- this.control.setVisible(true);
- } else {
- this.control.setVisible(false);
- }
- }
-}
-public void setImage (Image image) {
- checkWidget();
- if (image != null && image.equals(getImage())) return;
- super.setImage(image);
- parent.resetTabSize(true);
-}
-/**
- * Sets the image that is displayed if the tab item is disabled.
- * Null will clear the image.
- *
- * @param image the image to be displayed when the item is disabled or null
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setDisabledImage (Image image) {
- checkWidget();
- if (image != null && image.equals(getDisabledImage())) return;
- disabledImage = image;
- parent.redraw();
-}
-
-/**
- * Set the widget text.
- * <p>
- * This method sets the widget label. The label may include
- * mnemonic characters but must not contain line delimiters.
- *
- * @param string the new label for the widget
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setText (String string) {
- checkWidget();
- if (string.equals(getText())) return;
- super.setText(string);
- shortenedText = null;
- shortenedTextWidth = 0;
- parent.resetTabSize(false);
-}
-/**
- * Sets the receiver's tool tip text to the argument, which
- * may be null indicating that no tool tip text should be shown.
- *
- * @param string the new tool tip text (or null)
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setToolTipText (String string) {
- checkWidget();
- toolTipText = string;
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+public class CTabItem extends Item {
+ CTabFolder parent;
+ int x,y,width,height = 0;
+ String toolTipText;
+ Control control; // the tab page
+
+ private Image disabledImage;
+
+ // internal constants
+ static final int LEFT_MARGIN = 4;
+ static final int RIGHT_MARGIN = 4;
+ static final int TOP_MARGIN = 3;
+ static final int BOTTOM_MARGIN = 3;
+ private static final int INTERNAL_SPACING = 2;
+
+ private static final String ellipsis = "..."; //$NON-NLS-1$
+
+ String shortenedText;
+ int shortenedTextWidth;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>CTabFolder</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#getStyle
+ */
+public CTabItem (CTabFolder parent, int style) {
+ this(parent, style, parent.getItemCount());
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>CTabFolder</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#getStyle
+ */
+public CTabItem (CTabFolder parent, int style, int index) {
+ super (parent, checkStyle(style));
+ parent.createItem (this, index);
+}
+private static int checkStyle(int style) {
+ return SWT.NONE;
+}
+
+public void dispose () {
+ if (isDisposed()) return;
+ parent.destroyItem(this);
+ super.dispose();
+ parent = null;
+ control = null;
+ toolTipText = null;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding column rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+ //checkWidget();
+ return new Rectangle(x, y, width, height);
+}
+/**
+* Gets the control that is displayed in the content are of the tab item.
+*
+* @return the control
+*
+* @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
+* when called from the wrong thread
+* @exception SWTError(ERROR_WIDGET_DISPOSED)
+* when the widget has been disposed
+*/
+public Control getControl () {
+ checkWidget();
+ return control;
+}
+public Display getDisplay() {
+ if (parent == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED);
+ return parent.getDisplay();
+}
+/**
+ * Get the image displayed in the tab if the tab is disabled.
+ *
+ * @return the disabled image or null
+ */
+public Image getDisabledImage(){
+ //checkWidget();
+ return disabledImage;
+}
+/**
+ * Returns the receiver's parent, which must be a <code>CTabFolder</code>.
+ *
+ * @return the receiver's parent
+ */
+public CTabFolder getParent () {
+ //checkWidget();
+ return parent;
+}
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getToolTipText () {
+ checkWidget();
+ return toolTipText;
+}
+/**
+ * Paint the receiver.
+ */
+void onPaint(GC gc, boolean isSelected) {
+
+ if (width == 0 || height == 0) return;
+
+ Display display = getDisplay();
+ Color highlightShadow = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+ Color normalShadow = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+
+ int index = parent.indexOf(this);
+
+ if (isSelected) {
+
+ Rectangle bounds = null;
+ if (!parent.onBottom) {
+ if (index == parent.topTabIndex) {
+ bounds = new Rectangle(x + 1, y + 1, width - 2, height - 1);
+ } else {
+ bounds = new Rectangle(x + 2, y + 1, width - 3, height - 1);
+ }
+ } else {
+ if (index == parent.topTabIndex) {
+ bounds = new Rectangle(x + 1, y + 1, width - 2, height - 2);
+ } else {
+ bounds = new Rectangle(x + 2, y + 1, width - 3, height - 2);
+ }
+ }
+ if (parent.backgroundImage != null) {
+ // draw a background image behind the text
+ Rectangle imageRect = parent.backgroundImage.getBounds();
+ gc.drawImage(parent.backgroundImage, 0, 0, imageRect.width, imageRect.height,
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ } else if (parent.gradientColors != null) {
+ // draw a gradient behind the text
+ Color oldBackground = gc.getBackground();
+ if (parent.gradientColors.length == 1) {
+ if (parent.gradientColors[0] != null) gc.setBackground(parent.gradientColors[0]);
+ gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
+ } else {
+ Color oldForeground = gc.getForeground();
+ Color lastColor = parent.gradientColors[0];
+ if (lastColor == null) lastColor = oldBackground;
+ for (int i = 0, pos = 0; i < parent.gradientPercents.length; ++i) {
+ gc.setForeground(lastColor);
+ lastColor = parent.gradientColors[i + 1];
+ if (lastColor == null) lastColor = oldBackground;
+ gc.setBackground(lastColor);
+ int gradientWidth = (parent.gradientPercents[i] * bounds.width / 100) - pos;
+ gc.fillGradientRectangle(bounds.x + pos, bounds.y, gradientWidth, bounds.height, false);
+ pos += gradientWidth;
+ }
+ gc.setForeground(oldForeground);
+ }
+ gc.setBackground(oldBackground);
+ }
+
+ // draw tab lines
+ if (!parent.onBottom) {
+ gc.setForeground(normalShadow);
+ if (index != parent.topTabIndex) {
+ gc.drawLine(x + 1, y, x + 1, y);
+ gc.drawLine(x, y + 1, x, y + height - 2);
+ gc.drawLine(x, y + height - 1, x, y + height - 1);
+ }
+ gc.drawLine(x + width - 1, y, x + width - 1, y);
+ gc.drawLine(x + width, y + 1, x + width, y + height - 2);
+ gc.drawLine(x + width, y + height - 1, x + width, y + height - 1);
+
+ gc.setForeground(highlightShadow);
+ if (index != parent.topTabIndex) {
+ gc.drawLine(x + 2, y, x + 2, y);
+ gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
+ gc.drawLine(x + 1, y + height - 1, x + 1, y + height - 1);
+ } else {
+ gc.drawLine(x, y, x, y + height - 1);
+ }
+
+ gc.drawLine(x + width - 2, y, x + width - 2, y);
+ gc.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 2);
+ gc.drawLine(x + width - 1, y + height - 1, x + width - 1, y + height - 1);
+
+ // light line across top
+ if (index != parent.topTabIndex) {
+ gc.drawLine(x + 3, y, x + width - 3, y);
+ } else {
+ gc.drawLine(x + 1, y, x + width - 3, y);
+ }
+ } else {
+ gc.setForeground(normalShadow);
+ if (index != parent.topTabIndex) {
+ gc.drawLine(x, y, x, y);
+ gc.drawLine(x, y + 1, x, y + height - 2);
+ gc.drawLine(x + 1, y + height - 1, x + 1, y + height - 1);
+ }
+ gc.drawLine(x + width, y, x + width, y);
+ gc.drawLine(x + width, y + 1, x + width, y + height - 2);
+ gc.drawLine(x + width - 1, y + height - 1, x + width - 1, y + height - 1);
+
+ gc.setForeground(highlightShadow);
+ if (index != parent.topTabIndex) {
+ gc.drawLine(x + 1, y, x + 1, y);
+ gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
+ gc.drawLine(x + 2, y + height - 1, x + 2, y + height - 1);
+ } else {
+ gc.drawLine(x, y, x, y + height - 1);
+ }
+
+ gc.drawLine(x + width - 1, y, x + width - 1, y);
+ gc.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 2);
+ gc.drawLine(x + width - 2, y + height - 1, x + width - 2, y + height - 1);
+
+ // light line across top and bottom
+ if (index != parent.topTabIndex) {
+ gc.drawLine(x + 1, y, x + width - 2, y);
+ gc.drawLine(x + 2, y + height - 1, x + width - 3, y + height - 1);
+ } else {
+ gc.drawLine(x + 1, y, x + width - 2, y);
+ gc.drawLine(x + 1, y + height - 1, x + width - 3, y + height - 1);
+ }
+ }
+ if (parent.isFocusControl()) {
+ // draw a focus rectangle
+ int x1, y1, width1, height1;
+ if (!parent.onBottom) {
+ if (index == parent.topTabIndex) {
+ x1 = x + 1; y1 = y + 1; width1 = width - 2; height1 = height - 1;
+ } else {
+ x1 = x + 2; y1 = y + 1; width1 = width - 3; height1 = height - 1;
+ }
+ } else {
+ if (index == parent.topTabIndex) {
+ x1 = x + 1; y1 = y + 1; width1 = width - 2; height1 = height - 2;
+ } else {
+ x1 = x + 2; y1 = y + 1; width1 = width - 3; height1 = height - 2;
+ }
+ }
+ gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+ gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
+ gc.drawFocus(x1, y1, width1, height1);
+ }
+ } else {
+ // draw tab lines for unselected items
+ gc.setForeground(normalShadow);
+ if (!parent.onBottom) {
+ if (index != parent.topTabIndex && index != parent.getSelectionIndex() + 1) {
+ gc.drawLine(x, y, x, y + (height / 2));
+ }
+ } else {
+ if (index != parent.topTabIndex && index != parent.getSelectionIndex() + 1) {
+ gc.drawLine(x, y + (height / 2), x, y + height - 1);
+ }
+ }
+
+ }
+
+ // draw Image
+ int xDraw = x + LEFT_MARGIN;
+
+ Image image = getImage();
+ if (!isSelected && image != null) {
+ Image temp = getDisabledImage();
+ if (temp != null){
+ image = temp;
+ }
+ }
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds();
+ int imageX = xDraw;
+ int imageHeight = Math.min(height - BOTTOM_MARGIN - TOP_MARGIN, imageBounds.height);
+ int imageY = y + (height - imageHeight) / 2;
+ int imageWidth = imageBounds.width * imageHeight / imageBounds.height;
+ gc.drawImage(image,
+ imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height,
+ imageX, imageY, imageWidth, imageHeight);
+ xDraw += imageWidth + INTERNAL_SPACING;
+ }
+
+ // draw Text
+ int textWidth = x + width - xDraw - RIGHT_MARGIN;
+ if (isSelected && parent.showClose) {
+ textWidth = x + width - xDraw - parent.closeBar.getSize().x - RIGHT_MARGIN;
+ }
+ if (shortenedText == null || shortenedTextWidth != textWidth) {
+ shortenedText = shortenText(gc, getText(), textWidth);
+ shortenedTextWidth = textWidth;
+ }
+ String text = shortenedText;
+
+ if (isSelected && parent.selectionForeground != null) {
+ gc.setForeground(parent.selectionForeground);
+ } else {
+ gc.setForeground(parent.getForeground());
+ }
+ int textY = y + (height - gc.textExtent(text, SWT.DRAW_MNEMONIC).y) / 2;
+ gc.drawText(text, xDraw, textY, SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC);
+
+ gc.setForeground(parent.getForeground());
+}
+private static String shortenText(GC gc, String text, int width) {
+ if (gc.textExtent(text, SWT.DRAW_MNEMONIC).x <= width) return text;
+
+ int ellipseWidth = gc.textExtent(ellipsis, SWT.DRAW_MNEMONIC).x;
+ int length = text.length();
+ int end = length - 1;
+ while (end > 0) {
+ text = text.substring(0, end);
+ int l1 = gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
+ if (l1 + ellipseWidth <= width) {
+ return text + ellipsis;
+ }
+ end--;
+ }
+ return text + ellipsis;
+}
+/**
+ * Answer the preferred height of the receiver for the GC.
+ */
+int preferredHeight(GC gc) {
+ Image image = getImage();
+ int height = 0;
+ if (image != null) height = image.getBounds().height;
+ String text = getText();
+ height = Math.max(height, gc.textExtent(text, SWT.DRAW_MNEMONIC).y);
+ return height + TOP_MARGIN + BOTTOM_MARGIN;
+}
+/**
+ * Answer the preferred width of the receiver for the GC.
+ */
+int preferredWidth(GC gc) {
+ int width = 0;
+ Image image = getImage();
+ if (image != null) width += image.getBounds().width;
+ String text = getText();
+ if (text != null) {
+ if (image != null) width += INTERNAL_SPACING;
+ width += gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
+ }
+ if (parent.showClose) width += INTERNAL_SPACING + preferredHeight(gc); // closebar will be square and will fill preferred height
+ return width + LEFT_MARGIN + RIGHT_MARGIN;
+}
+/**
+ * Sets the control that is used to fill the client area of
+ * the tab folder when the user selects the tab item.
+ * <p>
+ * @param control the new control (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setControl (Control control) {
+ checkWidget();
+ if (control != null) {
+ if (control.isDisposed()) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ if (control.getParent() != parent) SWT.error (SWT.ERROR_INVALID_PARENT);
+ }
+ if (this.control != null && !this.control.isDisposed()) {
+ this.control.setVisible(false);
+ }
+ this.control = control;
+ if (this.control != null) {
+ int index = parent.indexOf (this);
+ if (index == parent.getSelectionIndex ()){
+ this.control.setBounds(parent.getClientArea ());
+ this.control.setVisible(true);
+ } else {
+ this.control.setVisible(false);
+ }
+ }
+}
+public void setImage (Image image) {
+ checkWidget();
+ if (image != null && image.equals(getImage())) return;
+ super.setImage(image);
+ parent.resetTabSize(true);
+}
+/**
+ * Sets the image that is displayed if the tab item is disabled.
+ * Null will clear the image.
+ *
+ * @param image the image to be displayed when the item is disabled or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDisabledImage (Image image) {
+ checkWidget();
+ if (image != null && image.equals(getDisabledImage())) return;
+ disabledImage = image;
+ parent.redraw();
+}
+
+/**
+ * Set the widget text.
+ * <p>
+ * This method sets the widget label. The label may include
+ * mnemonic characters but must not contain line delimiters.
+ *
+ * @param string the new label for the widget
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+ checkWidget();
+ if (string.equals(getText())) return;
+ super.setText(string);
+ shortenedText = null;
+ shortenedTextWidth = 0;
+ parent.resetTabSize(false);
+}
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setToolTipText (String string) {
+ checkWidget();
+ toolTipText = string;
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ControlEditor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ControlEditor.java
index 679db4762f..d681ce5d13 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ControlEditor.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ControlEditor.java
@@ -1,242 +1,242 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
-*
-* A ControlEditor is a manager for a Control that appears above a composite and tracks with the
-* moving and resizing of that composite. It can be used to display one control above
-* another control. This could be used when editing a control that does not have editing
-* capabilities by using a text editor or for launching a dialog by placing a button
-* above a control.
-*
-* <p> Here is an example of using a ControlEditor:
-*
-* <code><pre>
-* Canvas canvas = new Canvas(shell, SWT.BORDER);
-* canvas.setBounds(10, 10, 300, 300);
-* Color color = new Color(null, 255, 0, 0);
-* canvas.setBackground(color);
-* ControlEditor editor = new ControlEditor (canvas);
-* // The editor will be a button in the bottom right corner of the canvas.
-* // When selected, it will launch a Color dialog that will change the background
-* // of the canvas.
-* Button button = new Button(canvas, SWT.PUSH);
-* button.setText("Select Color...");
-* button.addSelectionListener (new SelectionAdapter() {
-* public void widgetSelected(SelectionEvent e) {
-* ColorDialog dialog = new ColorDialog(shell);
-* dialog.open();
-* RGB rgb = dialog.getRGB();
-* if (rgb != null) {
-* if (color != null) color.dispose();
-* color = new Color(null, rgb);
-* canvas.setBackground(color);
-* }
-*
-* }
-* });
-*
-* editor.horizontalAlignment = SWT.RIGHT;
-* editor.verticalAlignment = SWT.BOTTOM;
-* editor.grabHorizontal = false;
-* editor.grabVertical = false;
-* Point size = button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
-* editor.minimumWidth = size.x;
-* editor.minimumHeight = size.y;
-* editor.setEditor (button);
-* </pre></code>
-*/
-public class ControlEditor {
-
- /**
- * Specifies how the editor should be aligned relative to the control. Allowed values
- * are SWT.LEFT, SWT.RIGHT and SWT.CENTER. The default value is SWT.CENTER.
- */
- public int horizontalAlignment = SWT.CENTER;
-
- /**
- * Specifies whether the editor should be sized to use the entire width of the control.
- * True means resize the editor to the same width as the cell. False means do not adjust
- * the width of the editor. The default value is false.
- */
- public boolean grabHorizontal = false;
-
- /**
- * Specifies the minimum width the editor can have. This is used in association with
- * a true value of grabHorizontal. If the cell becomes smaller than the minimumWidth, the
- * editor will not made smaller than the minumum width value. The default value is 0.
- */
- public int minimumWidth = 0;
-
- /**
- * Specifies how the editor should be aligned relative to the control. Allowed values
- * are SWT.TOP, SWT.BOTTOM and SWT.CENTER. The default value is SWT.CENTER.
- */
- public int verticalAlignment = SWT.CENTER;
-
- /**
- * Specifies whether the editor should be sized to use the entire height of the control.
- * True means resize the editor to the same height as the underlying control. False means do not adjust
- * the height of the editor. The default value is false.
- */
- public boolean grabVertical = false;
-
- /**
- * Specifies the minimum height the editor can have. This is used in association with
- * a true value of grabVertical. If the control becomes smaller than the minimumHeight, the
- * editor will not made smaller than the minumum height value. The default value is 0.
- */
- public int minimumHeight = 0;
-
- Composite parent;
- Control editor;
- private boolean hadFocus;
- private Listener tableListener;
- private Listener scrollbarListener;
-/**
-* Creates a ControlEditor for the specified Composite.
-*
-* @param parent the Composite above which this editor will be displayed
-*
-*/
-public ControlEditor (Composite parent) {
- this.parent = parent;
-
- tableListener = new Listener() {
- public void handleEvent(Event e) {
- resize ();
- }
- };
- parent.addListener (SWT.Resize, tableListener);
-
- scrollbarListener = new Listener() {
- public void handleEvent(Event e) {
- scroll (e);
- }
- };
- ScrollBar hBar = parent.getHorizontalBar ();
- if (hBar != null) hBar.addListener (SWT.Selection, scrollbarListener);
- ScrollBar vBar = parent.getVerticalBar ();
- if (vBar != null) vBar.addListener (SWT.Selection, scrollbarListener);
-}
-Rectangle computeBounds () {
- Rectangle clientArea = parent.getClientArea();
- Rectangle editorRect = new Rectangle(clientArea.x, clientArea.y, minimumWidth, minimumHeight);
-
- if (grabHorizontal)
- editorRect.width = Math.max(clientArea.width, minimumWidth);
-
- if (grabVertical)
- editorRect.height = Math.max(clientArea.height, minimumHeight);
-
- switch (horizontalAlignment) {
- case SWT.RIGHT:
- editorRect.x += clientArea.width - editorRect.width;
- break;
- case SWT.LEFT:
- // do nothing - clientArea.x is the right answer
- break;
- default:
- // default is CENTER
- editorRect.x += (clientArea.width - editorRect.width)/2;
- }
-
- switch (verticalAlignment) {
- case SWT.BOTTOM:
- editorRect.y += clientArea.height - editorRect.height;
- break;
- case SWT.TOP:
- // do nothing - clientArea.y is the right answer
- break;
- default :
- // default is CENTER
- editorRect.y += (clientArea.height - editorRect.height)/2;
- }
-
-
- return editorRect;
-
-}
-/**
- * Removes all associations between the Editor and the underlying composite. The
- * composite and the editor Control are <b>not</b> disposed.
- */
-public void dispose () {
- if (!parent.isDisposed()) {
- parent.removeListener (SWT.Resize, tableListener);
- ScrollBar hBar = parent.getHorizontalBar ();
- if (hBar != null) hBar.removeListener (SWT.Selection, scrollbarListener);
- ScrollBar vBar = parent.getVerticalBar ();
- if (vBar != null) vBar.removeListener (SWT.Selection, scrollbarListener);
- }
-
- parent = null;
- editor = null;
- hadFocus = false;
- tableListener = null;
- scrollbarListener = null;
-}
-/**
-* Returns the Control that is displayed above the composite being edited.
-*
-* @return the Control that is displayed above the composite being edited
-*/
-public Control getEditor () {
- return editor;
-}
-/**
- * Lays out the control within the underlying composite. This
- * method should be called after changing one or more fields to
- * force the Editor to resize.
- *
- * @since 2.1
- */
-public void layout () {
- resize();
-}
-void resize () {
- if (editor == null || editor.isDisposed()) return;
- if (editor.getVisible ()) {
- hadFocus = editor.isFocusControl();
- } // this doesn't work because
- // resizing the column takes the focus away
- // before we get here
- editor.setBounds (computeBounds ());
- if (hadFocus) editor.setFocus ();
-}
-void scroll (Event e) {
- if (editor == null || editor.isDisposed()) return;
- editor.setBounds (computeBounds ());
-}
-/**
-* Specify the Control that is to be displayed.
-*
-* <p>Note: The Control provided as the editor <b>must</b> be created with its parent
-* being the Composite specified in the ControlEditor constructor.
-*
-* @param editor the Control that is displayed above the composite being edited
-*/
-public void setEditor (Control editor) {
-
- if (editor == null) {
- // this is the case where the caller is setting the editor to be blank
- // set all the values accordingly
- this.editor = null;
- return;
- }
-
- this.editor = editor;
- resize();
- editor.setVisible(true);
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+*
+* A ControlEditor is a manager for a Control that appears above a composite and tracks with the
+* moving and resizing of that composite. It can be used to display one control above
+* another control. This could be used when editing a control that does not have editing
+* capabilities by using a text editor or for launching a dialog by placing a button
+* above a control.
+*
+* <p> Here is an example of using a ControlEditor:
+*
+* <code><pre>
+* Canvas canvas = new Canvas(shell, SWT.BORDER);
+* canvas.setBounds(10, 10, 300, 300);
+* Color color = new Color(null, 255, 0, 0);
+* canvas.setBackground(color);
+* ControlEditor editor = new ControlEditor (canvas);
+* // The editor will be a button in the bottom right corner of the canvas.
+* // When selected, it will launch a Color dialog that will change the background
+* // of the canvas.
+* Button button = new Button(canvas, SWT.PUSH);
+* button.setText("Select Color...");
+* button.addSelectionListener (new SelectionAdapter() {
+* public void widgetSelected(SelectionEvent e) {
+* ColorDialog dialog = new ColorDialog(shell);
+* dialog.open();
+* RGB rgb = dialog.getRGB();
+* if (rgb != null) {
+* if (color != null) color.dispose();
+* color = new Color(null, rgb);
+* canvas.setBackground(color);
+* }
+*
+* }
+* });
+*
+* editor.horizontalAlignment = SWT.RIGHT;
+* editor.verticalAlignment = SWT.BOTTOM;
+* editor.grabHorizontal = false;
+* editor.grabVertical = false;
+* Point size = button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+* editor.minimumWidth = size.x;
+* editor.minimumHeight = size.y;
+* editor.setEditor (button);
+* </pre></code>
+*/
+public class ControlEditor {
+
+ /**
+ * Specifies how the editor should be aligned relative to the control. Allowed values
+ * are SWT.LEFT, SWT.RIGHT and SWT.CENTER. The default value is SWT.CENTER.
+ */
+ public int horizontalAlignment = SWT.CENTER;
+
+ /**
+ * Specifies whether the editor should be sized to use the entire width of the control.
+ * True means resize the editor to the same width as the cell. False means do not adjust
+ * the width of the editor. The default value is false.
+ */
+ public boolean grabHorizontal = false;
+
+ /**
+ * Specifies the minimum width the editor can have. This is used in association with
+ * a true value of grabHorizontal. If the cell becomes smaller than the minimumWidth, the
+ * editor will not made smaller than the minumum width value. The default value is 0.
+ */
+ public int minimumWidth = 0;
+
+ /**
+ * Specifies how the editor should be aligned relative to the control. Allowed values
+ * are SWT.TOP, SWT.BOTTOM and SWT.CENTER. The default value is SWT.CENTER.
+ */
+ public int verticalAlignment = SWT.CENTER;
+
+ /**
+ * Specifies whether the editor should be sized to use the entire height of the control.
+ * True means resize the editor to the same height as the underlying control. False means do not adjust
+ * the height of the editor. The default value is false.
+ */
+ public boolean grabVertical = false;
+
+ /**
+ * Specifies the minimum height the editor can have. This is used in association with
+ * a true value of grabVertical. If the control becomes smaller than the minimumHeight, the
+ * editor will not made smaller than the minumum height value. The default value is 0.
+ */
+ public int minimumHeight = 0;
+
+ Composite parent;
+ Control editor;
+ private boolean hadFocus;
+ private Listener tableListener;
+ private Listener scrollbarListener;
+/**
+* Creates a ControlEditor for the specified Composite.
+*
+* @param parent the Composite above which this editor will be displayed
+*
+*/
+public ControlEditor (Composite parent) {
+ this.parent = parent;
+
+ tableListener = new Listener() {
+ public void handleEvent(Event e) {
+ resize ();
+ }
+ };
+ parent.addListener (SWT.Resize, tableListener);
+
+ scrollbarListener = new Listener() {
+ public void handleEvent(Event e) {
+ scroll (e);
+ }
+ };
+ ScrollBar hBar = parent.getHorizontalBar ();
+ if (hBar != null) hBar.addListener (SWT.Selection, scrollbarListener);
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.addListener (SWT.Selection, scrollbarListener);
+}
+Rectangle computeBounds () {
+ Rectangle clientArea = parent.getClientArea();
+ Rectangle editorRect = new Rectangle(clientArea.x, clientArea.y, minimumWidth, minimumHeight);
+
+ if (grabHorizontal)
+ editorRect.width = Math.max(clientArea.width, minimumWidth);
+
+ if (grabVertical)
+ editorRect.height = Math.max(clientArea.height, minimumHeight);
+
+ switch (horizontalAlignment) {
+ case SWT.RIGHT:
+ editorRect.x += clientArea.width - editorRect.width;
+ break;
+ case SWT.LEFT:
+ // do nothing - clientArea.x is the right answer
+ break;
+ default:
+ // default is CENTER
+ editorRect.x += (clientArea.width - editorRect.width)/2;
+ }
+
+ switch (verticalAlignment) {
+ case SWT.BOTTOM:
+ editorRect.y += clientArea.height - editorRect.height;
+ break;
+ case SWT.TOP:
+ // do nothing - clientArea.y is the right answer
+ break;
+ default :
+ // default is CENTER
+ editorRect.y += (clientArea.height - editorRect.height)/2;
+ }
+
+
+ return editorRect;
+
+}
+/**
+ * Removes all associations between the Editor and the underlying composite. The
+ * composite and the editor Control are <b>not</b> disposed.
+ */
+public void dispose () {
+ if (!parent.isDisposed()) {
+ parent.removeListener (SWT.Resize, tableListener);
+ ScrollBar hBar = parent.getHorizontalBar ();
+ if (hBar != null) hBar.removeListener (SWT.Selection, scrollbarListener);
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.removeListener (SWT.Selection, scrollbarListener);
+ }
+
+ parent = null;
+ editor = null;
+ hadFocus = false;
+ tableListener = null;
+ scrollbarListener = null;
+}
+/**
+* Returns the Control that is displayed above the composite being edited.
+*
+* @return the Control that is displayed above the composite being edited
+*/
+public Control getEditor () {
+ return editor;
+}
+/**
+ * Lays out the control within the underlying composite. This
+ * method should be called after changing one or more fields to
+ * force the Editor to resize.
+ *
+ * @since 2.1
+ */
+public void layout () {
+ resize();
+}
+void resize () {
+ if (editor == null || editor.isDisposed()) return;
+ if (editor.getVisible ()) {
+ hadFocus = editor.isFocusControl();
+ } // this doesn't work because
+ // resizing the column takes the focus away
+ // before we get here
+ editor.setBounds (computeBounds ());
+ if (hadFocus) editor.setFocus ();
+}
+void scroll (Event e) {
+ if (editor == null || editor.isDisposed()) return;
+ editor.setBounds (computeBounds ());
+}
+/**
+* Specify the Control that is to be displayed.
+*
+* <p>Note: The Control provided as the editor <b>must</b> be created with its parent
+* being the Composite specified in the ControlEditor constructor.
+*
+* @param editor the Control that is displayed above the composite being edited
+*/
+public void setEditor (Control editor) {
+
+ if (editor == null) {
+ // this is the case where the caller is setting the editor to be blank
+ // set all the values accordingly
+ this.editor = null;
+ return;
+ }
+
+ this.editor = editor;
+ resize();
+ editor.setVisible(true);
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultContent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultContent.java
index d14aefd0da..b9de862635 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultContent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultContent.java
@@ -1,879 +1,879 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.internal.Compatibility;
-import org.eclipse.swt.widgets.*;
-import java.util.Vector;
-
-class DefaultContent implements StyledTextContent {
- private final static String LineDelimiter = System.getProperty("line.separator");
-
- Vector textListeners = new Vector(); // stores text listeners for event sending
- char[] textStore = new char[0]; // stores the actual text
- int gapStart = -1; // the character position start of the gap
- int gapEnd = -1; // the character position after the end of the gap
- int gapLine = -1; // the line on which the gap exists, the gap will always be associated
- // with one line
- int highWatermark = 300;
- int lowWatermark = 50;
-
- int[][] lines = new int[50][2]; // array of character positions and lengths representing
- // the lines of text
- int lineCount = 0; // the number of lines of text
- int expandExp = 1; // the expansion exponent, used to increase the lines array exponentially
- int replaceExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially
-
-/**
- * Creates a new DefaultContent and initializes it. A <code>StyledTextContent</> will always have
- * at least one empty line.
- */
-DefaultContent() {
- super();
- setText("");
-}
-/**
- * Adds a line to the end of the line indexes array. Increases the size of the array if necessary.
- * <code>lineCount</code> is updated to reflect the new entry.
- * <p>
- *
- * @param start the start of the line
- * @param length the length of the line
- */
-void addLineIndex(int start, int length) {
- int size = lines.length;
- if (lineCount == size) {
- // expand the lines by powers of 2
- int[][] newLines = new int[size+Compatibility.pow2(expandExp)][2];
- System.arraycopy(lines, 0, newLines, 0, size);
- lines = newLines;
- expandExp++;
- }
- int[] range = new int[] {start, length};
- lines[lineCount] = range;
- lineCount++;
-}
-/**
- * Adds a line index to the end of <code>linesArray</code>. Increases the
- * size of the array if necessary and returns a new array.
- * <p>
- *
- * @param start the start of the line
- * @param length the length of the line
- * @param linesArray the array to which to add the line index
- * @param count the position at which to add the line
- * @return a new array of line indexes
- */
-int[][] addLineIndex(int start, int length, int[][] linesArray, int count) {
- int size = linesArray.length;
- int[][] newLines = linesArray;
- if (count == size) {
- newLines = new int[size+Compatibility.pow2(replaceExpandExp)][2];
- replaceExpandExp++;
- System.arraycopy(linesArray, 0, newLines, 0, size);
- }
- int[] range = new int[] {start, length};
- newLines[count] = range;
- return newLines;
-}
-/**
- * Adds a <code>TextChangeListener</code> listening for
- * <code>TextChangingEvent</code> and <code>TextChangedEvent</code>. A
- * <code>TextChangingEvent</code> is sent before changes to the text occur.
- * A <code>TextChangedEvent</code> is sent after changes to the text
- * occured.
- * <p>
- *
- * @param listener the listener
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addTextChangeListener(TextChangeListener listener) {
- if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
- StyledTextListener typedListener = new StyledTextListener(listener);
- textListeners.addElement(typedListener);
-}
-/**
- * Adjusts the gap to accomodate a text change that is occurring.
- * <p>
- *
- * @param position the position at which a change is occurring
- * @param sizeHint the size of the change
- * @param line the line where the gap will go
- */
-void adjustGap(int position, int sizeHint, int line) {
- if (position == gapStart) {
- // text is being inserted at the gap position
- int size = (gapEnd - gapStart) - sizeHint;
- if (lowWatermark <= size && size <= highWatermark)
- return;
- } else if ((position + sizeHint == gapStart) && (sizeHint < 0)) {
- // text is being deleted at the gap position
- int size = (gapEnd - gapStart) - sizeHint;
- if (lowWatermark <= size && size <= highWatermark)
- return;
- }
- moveAndResizeGap(position, sizeHint, line);
-}
-/**
- * Calculates the indexes of each line in the text store. Assumes no gap exists.
- * Optimized to do less checking.
- */
-void indexLines(){
- int start = 0;
- lineCount = 0;
- int textLength = textStore.length;
- int i;
- for (i=start; i<textLength; i++) {
- char ch = textStore[i];
- if (ch == SWT.CR) {
- // see if the next character is a LF
- if (i + 1 < textLength) {
- ch = textStore[i+1];
- if (ch == SWT.LF) {
- i++;
- }
- }
- addLineIndex(start, i - start + 1);
- start = i + 1;
- } else if (ch == SWT.LF) {
- addLineIndex(start, i - start + 1);
- start = i + 1;
- }
- }
- addLineIndex(start, i - start);
-}
-/**
- * Returns whether or not the given character is a line delimiter. Both CR and LF
- * are valid line delimiters.
- * <p>
- *
- * @param ch the character to test
- * @return true if ch is a delimiter, false otherwise
- */
-boolean isDelimiter(char ch) {
- if (ch == SWT.CR) return true;
- if (ch == SWT.LF) return true;
- return false;
-}
-/**
- * Determine whether or not the replace operation is valid. DefaultContent will not allow
- * the /r/n line delimiter to be split or partially deleted.
- * <p>
- *
- * @param start start offset of text to replace
- * @param replaceLength start offset of text to replace
- * @param newText start offset of text to replace
- */
-protected boolean isValidReplace(int start, int replaceLength, String newText){
- if (replaceLength == 0) {
- // inserting text, see if the \r\n line delimiter is being split
- if (start == 0) return true;
- if (start == getCharCount()) return true;
- char before = getTextRange(start - 1, 1).charAt(0);
- if (before == '\r') {
- char after = getTextRange(start, 1).charAt(0);
- if (after == '\n') return false;
- }
- } else {
- // deleting text, see if part of a \r\n line delimiter is being deleted
- char startChar = getTextRange(start, 1).charAt(0);
- if (startChar == '\n') {
- // see if char before delete position is \r
- if (start != 0) {
- char before = getTextRange(start - 1, 1).charAt(0);
- if (before == '\r') return false;
- }
- }
- char endChar = getTextRange(start + replaceLength - 1, 1).charAt(0);
- if (endChar == '\r') {
- // see if char after delete position is \n
- if (start + replaceLength != getCharCount()) {
- char after = getTextRange(start + replaceLength, 1).charAt(0);
- if (after == '\n') return false;
- }
- }
- }
- return true;
-}
-/**
- * Calculates the indexes of each line of text in the given range.
- * <p>
- *
- * @param offset the logical start offset of the text lineate
- * @param length the length of the text to lineate, includes gap
- * @param numLines the number of lines to initially allocate for the line index array,
- * passed in for efficiency (the exact number of lines may be known)
- * @return a line indexes array where each line is identified by a start offset and
- * a length
- */
-int[][] indexLines(int offset, int length, int numLines){
- int[][] indexedLines = new int[numLines][2];
- int start = 0;
- int lineCnt = 0;
- int i;
- replaceExpandExp = 1;
- for (i=start; i<length; i++) {
- int location = i + offset;
- if ((location >= gapStart) && (location < gapEnd)) {
- // ignore the gap
- } else {
- char ch = textStore[location];
- if (ch == SWT.CR) {
- // see if the next character is a LF
- if (location+1 < textStore.length) {
- ch = textStore[location+1];
- if (ch == SWT.LF) {
- i++;
- }
- }
- indexedLines = addLineIndex(start, i - start + 1, indexedLines, lineCnt);
- lineCnt++;
- start = i + 1;
- } else if (ch == SWT.LF) {
- indexedLines = addLineIndex(start, i - start + 1, indexedLines, lineCnt);
- lineCnt++;
- start = i + 1;
- }
- }
- }
- int[][] newLines = new int[lineCnt+1][2];
- System.arraycopy(indexedLines, 0, newLines, 0, lineCnt);
- int[] range = new int[] {start, i - start};
- newLines[lineCnt]=range;
- return newLines;
-}
-/**
- * Inserts text.
- * <p>
- *
- * @param position the position at which to insert the text
- * @param length the text to insert
- */
-void insert(int position, String text) {
- if (text.length() == 0) return;
-
- int startLine = getLineAtOffset(position);
- int change = text.length();
- boolean endInsert = position == getCharCount();
- adjustGap(position, change, startLine);
-
- // during an insert the gap will be adjusted to start at
- // position and it will be associated with startline, the
- // inserted text will be placed in the gap
- int startLineOffset = getOffsetAtLine(startLine);
- // at this point, startLineLength will include the start line
- // and all of the newly inserted text
- int startLineLength = getPhysicalLine(startLine).length();
-
- if (change > 0) {
- // shrink gap
- gapStart += (change);
- for (int i = 0; i < text.length(); i++)
- textStore[position + i]= text.charAt(i);
- }
-
- // figure out the number of new lines that have been inserted
- int [][] newLines = indexLines(startLineOffset, startLineLength, 10);
- // only insert an empty line if it is the last line in the text
- int numNewLines = newLines.length - 1;
- if (newLines[numNewLines][1] == 0) {
- // last inserted line is a new line
- if (endInsert) {
- // insert happening at end of the text, leave numNewLines as
- // is since the last new line will not be concatenated with another
- // line
- numNewLines += 1;
- } else {
- numNewLines -= 1;
- }
- }
-
- // make room for the new lines
- expandLinesBy(numNewLines);
- // shift down the lines after the replace line
- for (int i = lineCount-1; i > startLine; i--) {
- lines[i + numNewLines]=lines[i];
- }
- // insert the new lines
- for (int i=0; i<numNewLines; i++) {
- newLines[i][0] += startLineOffset;
- lines[startLine + i]=newLines[i];
- }
- // update the last inserted line
- if (numNewLines < newLines.length) {
- newLines[numNewLines][0] += startLineOffset;
- lines[startLine + numNewLines] = newLines[numNewLines];
- }
-
- lineCount += numNewLines;
- gapLine = getLineAtPhysicalOffset(gapStart);
-}
-/**
- * Moves the gap and adjusts its size in anticipation of a text change.
- * The gap is resized to actual size + the specified size and moved to the given
- * position.
- * <p>
- *
- * @param position the position at which a change is occurring
- * @param sizeHint the size of the change
- * @param line the line where the gap should be put
- */
-void moveAndResizeGap(int position, int size, int newGapLine) {
- char[] content = null;
- int oldSize = gapEnd - gapStart;
- int newSize;
- if (size > 0) {
- newSize = highWatermark + size;
- } else {
- newSize = lowWatermark - size;
- }
- // remove the old gap from the lines information
- if (gapExists()) {
- // adjust the line length
- lines[gapLine][1] = lines[gapLine][1] - oldSize;
- // adjust the offsets of the lines after the gapLine
- for (int i=gapLine+1; i<lineCount; i++) {
- lines[i][0]=lines[i][0]-oldSize;
- }
- }
-
- if (newSize < 0) {
- if (oldSize > 0) {
- // removing the gap
- content = new char[textStore.length - oldSize];
- System.arraycopy(textStore, 0, content, 0, gapStart);
- System.arraycopy(textStore, gapEnd, content, gapStart, content.length - gapStart);
- textStore= content;
- }
- gapStart = gapEnd= position;
- return;
- }
- content = new char[textStore.length + (newSize - oldSize)];
- int newGapStart = position;
- int newGapEnd = newGapStart + newSize;
- if (oldSize == 0) {
- System.arraycopy(textStore, 0, content, 0, newGapStart);
- System.arraycopy(textStore, newGapStart, content, newGapEnd, content.length - newGapEnd);
- } else if (newGapStart < gapStart) {
- int delta = gapStart - newGapStart;
- System.arraycopy(textStore, 0, content, 0, newGapStart);
- System.arraycopy(textStore, newGapStart, content, newGapEnd, delta);
- System.arraycopy(textStore, gapEnd, content, newGapEnd + delta, textStore.length - gapEnd);
- } else {
- int delta = newGapStart - gapStart;
- System.arraycopy(textStore, 0, content, 0, gapStart);
- System.arraycopy(textStore, gapEnd, content, gapStart, delta);
- System.arraycopy(textStore, gapEnd + delta, content, newGapEnd, content.length - newGapEnd);
- }
- textStore = content;
- gapStart = newGapStart;
- gapEnd = newGapEnd;
-
- // add the new gap to the lines information
- if (gapExists()) {
- gapLine = newGapLine;
- // adjust the line length
- int gapLength = gapEnd - gapStart;
- lines[gapLine][1] = lines[gapLine][1] + (gapLength);
- // adjust the offsets of the lines after the gapLine
- for (int i=gapLine+1; i<lineCount; i++) {
- lines[i][0]=lines[i][0]+gapLength;
- }
- }
-}
-/**
- * Returns the number of lines that are in the specified text.
- * <p>
- *
- * @param startOffset the start of the text to lineate
- * @param length the length of the text to lineate
- * @return number of lines
- */
-int lineCount(int startOffset, int length){
- if (length == 0) {
- return 0;
- }
- int lineCnt = 0;
- int count = 0;
- int i = startOffset;
- if (i >= gapStart) {
- i += gapEnd - gapStart;
- }
- while (count < length) {
- if ((i >= gapStart) && (i < gapEnd)) {
- // ignore the gap
- } else {
- char ch = textStore[i];
- if (ch == SWT.CR) {
- // see if the next character is a LF
- if (i + 1 < textStore.length) {
- ch = textStore[i+1];
- if (ch == SWT.LF) {
- i++;
- count++;
- }
- }
- lineCnt++;
- } else if (ch == SWT.LF) {
- lineCnt++;
- }
- count++;
- }
- i++;
- }
- return lineCnt;
-}
-/**
- * Returns the number of lines that are in the specified text.
- * <p>
- *
- * @param text the text to lineate
- * @return number of lines in the text
- */
-int lineCount(String text){
- int lineCount = 0;
- int length = text.length();
- for (int i = 0; i < length; i++) {
- char ch = text.charAt(i);
- if (ch == SWT.CR) {
- if (i + 1 < length && text.charAt(i + 1) == SWT.LF) {
- i++;
- }
- lineCount++;
- }
- else
- if (ch == SWT.LF) {
- lineCount++;
- }
- }
- return lineCount;
-}
-/**
- * @return the logical length of the text store
- */
-public int getCharCount() {
- int length = gapEnd - gapStart;
- return (textStore.length - length);
-}
-/**
- * Returns the line at <code>index</code> without delimiters.
- * <p>
- *
- * @param index the index of the line to return
- * @return the logical line text (i.e., without the gap)
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when index is out of range</li>
- * </ul>
- */
-public String getLine(int index) {
- if ((index >= lineCount) || (index < 0)) error(SWT.ERROR_INVALID_ARGUMENT);
- int start = lines[index][0];
- int length = lines[index][1];
- int end = start + length - 1;
- if (!gapExists() || (end < gapStart) || (start >= gapEnd)) {
- // line is before or after the gap
- while ((length - 1 >= 0) && isDelimiter(textStore[start+length-1])) {
- length--;
- }
- return new String(textStore, start, length);
- } else {
- // gap is in the specified range, strip out the gap
- StringBuffer buf = new StringBuffer();
- int gapLength = gapEnd - gapStart;
- buf.append(textStore, start, gapStart - start);
- buf.append(textStore, gapEnd, length - gapLength - (gapStart - start));
- length = buf.length();
- while ((length - 1 >=0) && isDelimiter(buf.charAt(length-1))) {
- length--;
- }
- return Compatibility.substring(buf, 0, length);
- }
-}
-/**
- * Returns the line delimiter that should be used by the StyledText
- * widget when inserting new lines. This delimiter may be different than the
- * delimiter that is used by the <code>StyledTextContent</code> interface.
- * <p>
- *
- * @return the platform line delimiter as specified in the line.separator
- * system property.
- */
-public String getLineDelimiter() {
- return LineDelimiter;
-}
-/**
- * Returns the line at the given index with delimiters.
- * <p>
- * @param index the index of the line to return
- * @return the logical line text (i.e., without the gap) with delimiters
- */
-String getFullLine(int index) {
- int start = lines[index][0];
- int length = lines[index][1];
- int end = start + length - 1;
- if (!gapExists() || (end < gapStart) || (start >= gapEnd)) {
- // line is before or after the gap
- return new String(textStore, start, length);
- } else {
- // gap is in the specified range, strip out the gap
- StringBuffer buf = new StringBuffer();
- int gapLength = gapEnd - gapStart;
- buf.append(textStore, start, gapStart - start);
- buf.append(textStore, gapEnd, length - gapLength - (gapStart - start));
- return buf.toString();
- }
-}
-/**
- * Returns the physical line at the given index (i.e., with delimiters and the gap).
- * <p>
- *
- * @param index the line index
- * @return the physical line
- */
-String getPhysicalLine(int index) {
- int start = lines[index][0];
- int length = lines[index][1];
- return getPhysicalText(start, length);
-}
-/**
- * @return the number of lines in the text store
- */
-public int getLineCount(){
- return lineCount;
-}
-/**
- * Returns the line at the given offset.
- * <p>
- *
- * @param charPosition logical character offset (i.e., does not include gap)
- * @return the line index
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when charPosition is out of range</li>
- * </ul>
- */
-public int getLineAtOffset(int charPosition){
- int position;
- if ((charPosition > getCharCount()) || (charPosition < 0)) error(SWT.ERROR_INVALID_ARGUMENT);
- if (charPosition < gapStart) {
- // position is before the gap
- position = charPosition;
- } else {
- // position includes the gap
- position = charPosition + (gapEnd - gapStart);
- }
-
- // if last line and the line is not empty you can ask for
- // a position that doesn't exist (the one to the right of the
- // last character) - for inserting
- if (lineCount > 0) {
- int lastLine = lineCount - 1;
- if (position == lines[lastLine][0] + lines[lastLine][1])
- return lastLine;
- }
-
- int high = lineCount;
- int low = -1;
- int index = lineCount;
- while (high - low > 1) {
- index = (high + low) / 2;
- int lineStart = lines[index][0];
- int lineEnd = lineStart + lines[index][1] - 1;
- if (position <= lineStart) {
- high = index;
- } else if (position <= lineEnd) {
- high = index;
- break;
- } else {
- low = index;
- }
- }
-
- return high;
-}
-/**
- * Returns the line index at the given physical offset.
- * <p>
- *
- * @param position physical character offset (i.e., includes gap)
- * @return the line index
- */
-int getLineAtPhysicalOffset(int position){
- int high = lineCount;
- int low = -1;
- int index = lineCount;
- while (high - low > 1) {
- index = (high + low) / 2;
- int lineStart = lines[index][0];
- int lineEnd = lineStart + lines[index][1] - 1;
- if (position <= lineStart) {
- high = index;
- } else if (position <= lineEnd) {
- high = index;
- break;
- } else {
- low = index;
- }
- }
- return high;
-}
-/**
- * Returns the logical offset of the given line.
- * <p>
- *
- * @param lineIndex index of line
- * @return the logical starting offset of the line. When there are not any lines,
- * getOffsetAtLine(0) is a valid call that should answer 0.
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when lineIndex is out of range</li>
- * </ul>
- */
-public int getOffsetAtLine(int lineIndex) {
- if (lineIndex == 0) return 0;
- if ((lineIndex >= lineCount) || (lineIndex < 0)) error(SWT.ERROR_INVALID_ARGUMENT);
- int start = lines[lineIndex][0];
- if (start > gapEnd) {
- return start - (gapEnd - gapStart);
- } else {
- return start;
- }
-}
-/**
- * Increases the line indexes array to accomodate more lines.
- * <p>
- *
- * @param numLines the number to increase the array by
- */
-void expandLinesBy(int numLines) {
- int size = lines.length;
- if (size - lineCount >= numLines) {
- return;
- }
- int[][] newLines = new int[size+Math.max(10, numLines)][2];
- System.arraycopy(lines, 0, newLines, 0, size);
- lines = newLines;
-}
-/**
- * Reports an SWT error.
- * <p>
- *
- * @param code the error code
- */
-void error (int code) {
- SWT.error(code);
-}
-/**
- * Returns whether or not a gap exists in the text store.
- * <p>
- *
- * @return true if gap exists, false otherwise
- */
-boolean gapExists() {
- return gapStart != gapEnd;
-}
-
-/**
- * Returns a string representing the continous content of
- * the text store.
- * <p>
- *
- * @param start the physical start offset of the text to return
- * @param length the physical length of the text to return
- * @return the text
- */
-String getPhysicalText(int start, int length) {
- return new String(textStore, start, length);
-}
-/**
- * Returns a string representing the logical content of
- * the text store (i.e., gap stripped out).
- * <p>
- *
- * @param start the logical start offset of the text to return
- * @param length the logical length of the text to return
- * @return the text
- */
-public String getTextRange(int start, int length) {
- if (textStore == null)
- return "";
- if (length == 0)
- return "";
- int end= start + length;
- if (!gapExists() || (end < gapStart))
- return new String(textStore, start, length);
- if (gapStart < start) {
- int gapLength= gapEnd - gapStart;
- return new String(textStore, start + gapLength , length);
- }
- StringBuffer buf = new StringBuffer();
- buf.append(textStore, start, gapStart - start);
- buf.append(textStore, gapEnd, end - gapStart);
- return buf.toString();
-}
-/**
- * Removes the specified <code>TextChangeListener</code>.
- * <p>
- *
- * @param listener the listener
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeTextChangeListener(TextChangeListener listener){
- if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
- for (int i=0; i<textListeners.size(); i++) {
- TypedListener typedListener = (TypedListener) textListeners.elementAt(i);
- if (typedListener.getEventListener () == listener) {
- textListeners.removeElementAt(i);
- break;
- }
- }
-}
-/**
- * Replaces the text with <code>newText</code> starting at position <code>start</code>
- * for a length of <code>replaceLength</code>. Notifies the appropriate listeners.
- * <p>
- *
- * When sending the TextChangingEvent, <code>newLineCount</code> is the number of
- * lines that are going to be inserted and <code>replaceLineCount</code> is
- * the number of lines that are going to be deleted, based on the change
- * that occurs visually. For example:
- * <ul>
- * <li>(replaceText,newText) ==> (replaceLineCount,newLineCount)
- * <li>("","\n") ==> (0,1)
- * <li>("\n\n","a") ==> (2,0)
- * </ul>
- * </p>
- *
- * @param start start offset of text to replace
- * @param replaceLength start offset of text to replace
- * @param newText start offset of text to replace
- *
- * @exception SWTException <ul>
- * <li>ERROR_INVALID_ARGUMENT when the text change results in a multi byte
- * line delimiter being split or partially deleted. Splitting a line
- * delimiter by inserting text between the CR and LF characters of the
- * \r\n delimiter or deleting part of this line delimiter is not supported</li>
- * </ul>
- */
-public void replaceTextRange(int start, int replaceLength, String newText){
- // check for invalid replace operations
- if (!isValidReplace(start, replaceLength, newText)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
-
- // inform listeners
- StyledTextEvent event = new StyledTextEvent(this);
- event.type = StyledText.TextChanging;
- event.start = start;
- event.replaceLineCount = lineCount(start, replaceLength);
- event.text = newText;
- event.newLineCount = lineCount(newText);
- event.replaceCharCount = replaceLength;
- event.newCharCount = newText.length();
- sendTextEvent(event);
-
- // first delete the text to be replaced
- delete(start, replaceLength, event.replaceLineCount + 1);
- // then insert the new text
- insert(start, newText);
- // inform listeners
- event = new StyledTextEvent(this);
- event.type = StyledText.TextChanged;
- sendTextEvent(event);
- // printLines();
-}
-/**
- * Sends the text listeners the TextChanged event.
- */
-void sendTextEvent(StyledTextEvent event) {
- for (int i=0; i<textListeners.size(); i++) {
- ((StyledTextListener)textListeners.elementAt(i)).handleEvent(event);
- }
-}
-/**
- * Sets the content to text and removes the gap since there are no sensible predictions
- * about where the next change will occur.
- * <p>
- *
- * @param text the text
- */
-public void setText (String text){
- textStore = text.toCharArray();
- gapStart = -1;
- gapEnd = -1;
- expandExp = 1;
- indexLines();
- StyledTextEvent event = new StyledTextEvent(this);
- event.type = StyledText.TextSet;
- event.text = "";
- sendTextEvent(event);
-}
-/**
- * Deletes text.
- * <p>
- * @param position the position at which the text to delete starts
- * @param length the length of the text to delete
- * @param numLines the number of lines that are being deleted
- */
-void delete(int position, int length, int numLines) {
- if (length == 0) return;
-
- int startLine = getLineAtOffset(position);
- int startLineOffset = getOffsetAtLine(startLine);
- int endLine = getLineAtOffset(position + length);
-
- String endText = "";
- boolean splittingDelimiter = false;
- if (position + length < getCharCount()) {
- endText = getTextRange(position + length - 1, 2);
- if ((endText.charAt(0) == SWT.CR) && (endText.charAt(1) == SWT.LF)) {
- splittingDelimiter = true;
- }
- }
-
- adjustGap(position + length, -length, startLine);
- int [][] oldLines = indexLines(position, length + (gapEnd - gapStart), numLines);
-
- // enlarge the gap - the gap can be enlarged either to the
- // right or left
- if (position + length == gapStart) {
- gapStart -= length;
- } else {
- gapEnd += length;
- }
-
- // figure out the length of the new concatenated line, do so by
- // finding the first line delmiter after position
- int j = position;
- boolean eol = false;
- while (j < textStore.length && !eol) {
- if (j < gapStart || j >= gapEnd) {
- char ch = textStore[j];
- if (isDelimiter(ch)) {
- if (j + 1 < textStore.length)
- if (ch == SWT.CR && (textStore[j+1] == SWT.LF))
- j++;
- eol = true;
- }
- }
- j++;
- }
- // update the line where the deletion started
- lines[startLine][1] = (position - startLineOffset) + (j - position);
- // figure out the number of lines that have been deleted
- int numOldLines = oldLines.length - 1;
- if (splittingDelimiter) numOldLines -= 1;
- // shift up the lines after the last deleted line, no need to update
- // the offset or length of the lines
- for (int i = endLine + 1; i < lineCount; i++) {
- lines[i - numOldLines]=lines[i];
- }
- lineCount -= numOldLines;
- gapLine = getLineAtPhysicalOffset(gapStart);
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.internal.Compatibility;
+import org.eclipse.swt.widgets.*;
+import java.util.Vector;
+
+class DefaultContent implements StyledTextContent {
+ private final static String LineDelimiter = System.getProperty("line.separator");
+
+ Vector textListeners = new Vector(); // stores text listeners for event sending
+ char[] textStore = new char[0]; // stores the actual text
+ int gapStart = -1; // the character position start of the gap
+ int gapEnd = -1; // the character position after the end of the gap
+ int gapLine = -1; // the line on which the gap exists, the gap will always be associated
+ // with one line
+ int highWatermark = 300;
+ int lowWatermark = 50;
+
+ int[][] lines = new int[50][2]; // array of character positions and lengths representing
+ // the lines of text
+ int lineCount = 0; // the number of lines of text
+ int expandExp = 1; // the expansion exponent, used to increase the lines array exponentially
+ int replaceExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially
+
+/**
+ * Creates a new DefaultContent and initializes it. A <code>StyledTextContent</> will always have
+ * at least one empty line.
+ */
+DefaultContent() {
+ super();
+ setText("");
+}
+/**
+ * Adds a line to the end of the line indexes array. Increases the size of the array if necessary.
+ * <code>lineCount</code> is updated to reflect the new entry.
+ * <p>
+ *
+ * @param start the start of the line
+ * @param length the length of the line
+ */
+void addLineIndex(int start, int length) {
+ int size = lines.length;
+ if (lineCount == size) {
+ // expand the lines by powers of 2
+ int[][] newLines = new int[size+Compatibility.pow2(expandExp)][2];
+ System.arraycopy(lines, 0, newLines, 0, size);
+ lines = newLines;
+ expandExp++;
+ }
+ int[] range = new int[] {start, length};
+ lines[lineCount] = range;
+ lineCount++;
+}
+/**
+ * Adds a line index to the end of <code>linesArray</code>. Increases the
+ * size of the array if necessary and returns a new array.
+ * <p>
+ *
+ * @param start the start of the line
+ * @param length the length of the line
+ * @param linesArray the array to which to add the line index
+ * @param count the position at which to add the line
+ * @return a new array of line indexes
+ */
+int[][] addLineIndex(int start, int length, int[][] linesArray, int count) {
+ int size = linesArray.length;
+ int[][] newLines = linesArray;
+ if (count == size) {
+ newLines = new int[size+Compatibility.pow2(replaceExpandExp)][2];
+ replaceExpandExp++;
+ System.arraycopy(linesArray, 0, newLines, 0, size);
+ }
+ int[] range = new int[] {start, length};
+ newLines[count] = range;
+ return newLines;
+}
+/**
+ * Adds a <code>TextChangeListener</code> listening for
+ * <code>TextChangingEvent</code> and <code>TextChangedEvent</code>. A
+ * <code>TextChangingEvent</code> is sent before changes to the text occur.
+ * A <code>TextChangedEvent</code> is sent after changes to the text
+ * occured.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addTextChangeListener(TextChangeListener listener) {
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ StyledTextListener typedListener = new StyledTextListener(listener);
+ textListeners.addElement(typedListener);
+}
+/**
+ * Adjusts the gap to accomodate a text change that is occurring.
+ * <p>
+ *
+ * @param position the position at which a change is occurring
+ * @param sizeHint the size of the change
+ * @param line the line where the gap will go
+ */
+void adjustGap(int position, int sizeHint, int line) {
+ if (position == gapStart) {
+ // text is being inserted at the gap position
+ int size = (gapEnd - gapStart) - sizeHint;
+ if (lowWatermark <= size && size <= highWatermark)
+ return;
+ } else if ((position + sizeHint == gapStart) && (sizeHint < 0)) {
+ // text is being deleted at the gap position
+ int size = (gapEnd - gapStart) - sizeHint;
+ if (lowWatermark <= size && size <= highWatermark)
+ return;
+ }
+ moveAndResizeGap(position, sizeHint, line);
+}
+/**
+ * Calculates the indexes of each line in the text store. Assumes no gap exists.
+ * Optimized to do less checking.
+ */
+void indexLines(){
+ int start = 0;
+ lineCount = 0;
+ int textLength = textStore.length;
+ int i;
+ for (i=start; i<textLength; i++) {
+ char ch = textStore[i];
+ if (ch == SWT.CR) {
+ // see if the next character is a LF
+ if (i + 1 < textLength) {
+ ch = textStore[i+1];
+ if (ch == SWT.LF) {
+ i++;
+ }
+ }
+ addLineIndex(start, i - start + 1);
+ start = i + 1;
+ } else if (ch == SWT.LF) {
+ addLineIndex(start, i - start + 1);
+ start = i + 1;
+ }
+ }
+ addLineIndex(start, i - start);
+}
+/**
+ * Returns whether or not the given character is a line delimiter. Both CR and LF
+ * are valid line delimiters.
+ * <p>
+ *
+ * @param ch the character to test
+ * @return true if ch is a delimiter, false otherwise
+ */
+boolean isDelimiter(char ch) {
+ if (ch == SWT.CR) return true;
+ if (ch == SWT.LF) return true;
+ return false;
+}
+/**
+ * Determine whether or not the replace operation is valid. DefaultContent will not allow
+ * the /r/n line delimiter to be split or partially deleted.
+ * <p>
+ *
+ * @param start start offset of text to replace
+ * @param replaceLength start offset of text to replace
+ * @param newText start offset of text to replace
+ */
+protected boolean isValidReplace(int start, int replaceLength, String newText){
+ if (replaceLength == 0) {
+ // inserting text, see if the \r\n line delimiter is being split
+ if (start == 0) return true;
+ if (start == getCharCount()) return true;
+ char before = getTextRange(start - 1, 1).charAt(0);
+ if (before == '\r') {
+ char after = getTextRange(start, 1).charAt(0);
+ if (after == '\n') return false;
+ }
+ } else {
+ // deleting text, see if part of a \r\n line delimiter is being deleted
+ char startChar = getTextRange(start, 1).charAt(0);
+ if (startChar == '\n') {
+ // see if char before delete position is \r
+ if (start != 0) {
+ char before = getTextRange(start - 1, 1).charAt(0);
+ if (before == '\r') return false;
+ }
+ }
+ char endChar = getTextRange(start + replaceLength - 1, 1).charAt(0);
+ if (endChar == '\r') {
+ // see if char after delete position is \n
+ if (start + replaceLength != getCharCount()) {
+ char after = getTextRange(start + replaceLength, 1).charAt(0);
+ if (after == '\n') return false;
+ }
+ }
+ }
+ return true;
+}
+/**
+ * Calculates the indexes of each line of text in the given range.
+ * <p>
+ *
+ * @param offset the logical start offset of the text lineate
+ * @param length the length of the text to lineate, includes gap
+ * @param numLines the number of lines to initially allocate for the line index array,
+ * passed in for efficiency (the exact number of lines may be known)
+ * @return a line indexes array where each line is identified by a start offset and
+ * a length
+ */
+int[][] indexLines(int offset, int length, int numLines){
+ int[][] indexedLines = new int[numLines][2];
+ int start = 0;
+ int lineCnt = 0;
+ int i;
+ replaceExpandExp = 1;
+ for (i=start; i<length; i++) {
+ int location = i + offset;
+ if ((location >= gapStart) && (location < gapEnd)) {
+ // ignore the gap
+ } else {
+ char ch = textStore[location];
+ if (ch == SWT.CR) {
+ // see if the next character is a LF
+ if (location+1 < textStore.length) {
+ ch = textStore[location+1];
+ if (ch == SWT.LF) {
+ i++;
+ }
+ }
+ indexedLines = addLineIndex(start, i - start + 1, indexedLines, lineCnt);
+ lineCnt++;
+ start = i + 1;
+ } else if (ch == SWT.LF) {
+ indexedLines = addLineIndex(start, i - start + 1, indexedLines, lineCnt);
+ lineCnt++;
+ start = i + 1;
+ }
+ }
+ }
+ int[][] newLines = new int[lineCnt+1][2];
+ System.arraycopy(indexedLines, 0, newLines, 0, lineCnt);
+ int[] range = new int[] {start, i - start};
+ newLines[lineCnt]=range;
+ return newLines;
+}
+/**
+ * Inserts text.
+ * <p>
+ *
+ * @param position the position at which to insert the text
+ * @param length the text to insert
+ */
+void insert(int position, String text) {
+ if (text.length() == 0) return;
+
+ int startLine = getLineAtOffset(position);
+ int change = text.length();
+ boolean endInsert = position == getCharCount();
+ adjustGap(position, change, startLine);
+
+ // during an insert the gap will be adjusted to start at
+ // position and it will be associated with startline, the
+ // inserted text will be placed in the gap
+ int startLineOffset = getOffsetAtLine(startLine);
+ // at this point, startLineLength will include the start line
+ // and all of the newly inserted text
+ int startLineLength = getPhysicalLine(startLine).length();
+
+ if (change > 0) {
+ // shrink gap
+ gapStart += (change);
+ for (int i = 0; i < text.length(); i++)
+ textStore[position + i]= text.charAt(i);
+ }
+
+ // figure out the number of new lines that have been inserted
+ int [][] newLines = indexLines(startLineOffset, startLineLength, 10);
+ // only insert an empty line if it is the last line in the text
+ int numNewLines = newLines.length - 1;
+ if (newLines[numNewLines][1] == 0) {
+ // last inserted line is a new line
+ if (endInsert) {
+ // insert happening at end of the text, leave numNewLines as
+ // is since the last new line will not be concatenated with another
+ // line
+ numNewLines += 1;
+ } else {
+ numNewLines -= 1;
+ }
+ }
+
+ // make room for the new lines
+ expandLinesBy(numNewLines);
+ // shift down the lines after the replace line
+ for (int i = lineCount-1; i > startLine; i--) {
+ lines[i + numNewLines]=lines[i];
+ }
+ // insert the new lines
+ for (int i=0; i<numNewLines; i++) {
+ newLines[i][0] += startLineOffset;
+ lines[startLine + i]=newLines[i];
+ }
+ // update the last inserted line
+ if (numNewLines < newLines.length) {
+ newLines[numNewLines][0] += startLineOffset;
+ lines[startLine + numNewLines] = newLines[numNewLines];
+ }
+
+ lineCount += numNewLines;
+ gapLine = getLineAtPhysicalOffset(gapStart);
+}
+/**
+ * Moves the gap and adjusts its size in anticipation of a text change.
+ * The gap is resized to actual size + the specified size and moved to the given
+ * position.
+ * <p>
+ *
+ * @param position the position at which a change is occurring
+ * @param sizeHint the size of the change
+ * @param line the line where the gap should be put
+ */
+void moveAndResizeGap(int position, int size, int newGapLine) {
+ char[] content = null;
+ int oldSize = gapEnd - gapStart;
+ int newSize;
+ if (size > 0) {
+ newSize = highWatermark + size;
+ } else {
+ newSize = lowWatermark - size;
+ }
+ // remove the old gap from the lines information
+ if (gapExists()) {
+ // adjust the line length
+ lines[gapLine][1] = lines[gapLine][1] - oldSize;
+ // adjust the offsets of the lines after the gapLine
+ for (int i=gapLine+1; i<lineCount; i++) {
+ lines[i][0]=lines[i][0]-oldSize;
+ }
+ }
+
+ if (newSize < 0) {
+ if (oldSize > 0) {
+ // removing the gap
+ content = new char[textStore.length - oldSize];
+ System.arraycopy(textStore, 0, content, 0, gapStart);
+ System.arraycopy(textStore, gapEnd, content, gapStart, content.length - gapStart);
+ textStore= content;
+ }
+ gapStart = gapEnd= position;
+ return;
+ }
+ content = new char[textStore.length + (newSize - oldSize)];
+ int newGapStart = position;
+ int newGapEnd = newGapStart + newSize;
+ if (oldSize == 0) {
+ System.arraycopy(textStore, 0, content, 0, newGapStart);
+ System.arraycopy(textStore, newGapStart, content, newGapEnd, content.length - newGapEnd);
+ } else if (newGapStart < gapStart) {
+ int delta = gapStart - newGapStart;
+ System.arraycopy(textStore, 0, content, 0, newGapStart);
+ System.arraycopy(textStore, newGapStart, content, newGapEnd, delta);
+ System.arraycopy(textStore, gapEnd, content, newGapEnd + delta, textStore.length - gapEnd);
+ } else {
+ int delta = newGapStart - gapStart;
+ System.arraycopy(textStore, 0, content, 0, gapStart);
+ System.arraycopy(textStore, gapEnd, content, gapStart, delta);
+ System.arraycopy(textStore, gapEnd + delta, content, newGapEnd, content.length - newGapEnd);
+ }
+ textStore = content;
+ gapStart = newGapStart;
+ gapEnd = newGapEnd;
+
+ // add the new gap to the lines information
+ if (gapExists()) {
+ gapLine = newGapLine;
+ // adjust the line length
+ int gapLength = gapEnd - gapStart;
+ lines[gapLine][1] = lines[gapLine][1] + (gapLength);
+ // adjust the offsets of the lines after the gapLine
+ for (int i=gapLine+1; i<lineCount; i++) {
+ lines[i][0]=lines[i][0]+gapLength;
+ }
+ }
+}
+/**
+ * Returns the number of lines that are in the specified text.
+ * <p>
+ *
+ * @param startOffset the start of the text to lineate
+ * @param length the length of the text to lineate
+ * @return number of lines
+ */
+int lineCount(int startOffset, int length){
+ if (length == 0) {
+ return 0;
+ }
+ int lineCnt = 0;
+ int count = 0;
+ int i = startOffset;
+ if (i >= gapStart) {
+ i += gapEnd - gapStart;
+ }
+ while (count < length) {
+ if ((i >= gapStart) && (i < gapEnd)) {
+ // ignore the gap
+ } else {
+ char ch = textStore[i];
+ if (ch == SWT.CR) {
+ // see if the next character is a LF
+ if (i + 1 < textStore.length) {
+ ch = textStore[i+1];
+ if (ch == SWT.LF) {
+ i++;
+ count++;
+ }
+ }
+ lineCnt++;
+ } else if (ch == SWT.LF) {
+ lineCnt++;
+ }
+ count++;
+ }
+ i++;
+ }
+ return lineCnt;
+}
+/**
+ * Returns the number of lines that are in the specified text.
+ * <p>
+ *
+ * @param text the text to lineate
+ * @return number of lines in the text
+ */
+int lineCount(String text){
+ int lineCount = 0;
+ int length = text.length();
+ for (int i = 0; i < length; i++) {
+ char ch = text.charAt(i);
+ if (ch == SWT.CR) {
+ if (i + 1 < length && text.charAt(i + 1) == SWT.LF) {
+ i++;
+ }
+ lineCount++;
+ }
+ else
+ if (ch == SWT.LF) {
+ lineCount++;
+ }
+ }
+ return lineCount;
+}
+/**
+ * @return the logical length of the text store
+ */
+public int getCharCount() {
+ int length = gapEnd - gapStart;
+ return (textStore.length - length);
+}
+/**
+ * Returns the line at <code>index</code> without delimiters.
+ * <p>
+ *
+ * @param index the index of the line to return
+ * @return the logical line text (i.e., without the gap)
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when index is out of range</li>
+ * </ul>
+ */
+public String getLine(int index) {
+ if ((index >= lineCount) || (index < 0)) error(SWT.ERROR_INVALID_ARGUMENT);
+ int start = lines[index][0];
+ int length = lines[index][1];
+ int end = start + length - 1;
+ if (!gapExists() || (end < gapStart) || (start >= gapEnd)) {
+ // line is before or after the gap
+ while ((length - 1 >= 0) && isDelimiter(textStore[start+length-1])) {
+ length--;
+ }
+ return new String(textStore, start, length);
+ } else {
+ // gap is in the specified range, strip out the gap
+ StringBuffer buf = new StringBuffer();
+ int gapLength = gapEnd - gapStart;
+ buf.append(textStore, start, gapStart - start);
+ buf.append(textStore, gapEnd, length - gapLength - (gapStart - start));
+ length = buf.length();
+ while ((length - 1 >=0) && isDelimiter(buf.charAt(length-1))) {
+ length--;
+ }
+ return Compatibility.substring(buf, 0, length);
+ }
+}
+/**
+ * Returns the line delimiter that should be used by the StyledText
+ * widget when inserting new lines. This delimiter may be different than the
+ * delimiter that is used by the <code>StyledTextContent</code> interface.
+ * <p>
+ *
+ * @return the platform line delimiter as specified in the line.separator
+ * system property.
+ */
+public String getLineDelimiter() {
+ return LineDelimiter;
+}
+/**
+ * Returns the line at the given index with delimiters.
+ * <p>
+ * @param index the index of the line to return
+ * @return the logical line text (i.e., without the gap) with delimiters
+ */
+String getFullLine(int index) {
+ int start = lines[index][0];
+ int length = lines[index][1];
+ int end = start + length - 1;
+ if (!gapExists() || (end < gapStart) || (start >= gapEnd)) {
+ // line is before or after the gap
+ return new String(textStore, start, length);
+ } else {
+ // gap is in the specified range, strip out the gap
+ StringBuffer buf = new StringBuffer();
+ int gapLength = gapEnd - gapStart;
+ buf.append(textStore, start, gapStart - start);
+ buf.append(textStore, gapEnd, length - gapLength - (gapStart - start));
+ return buf.toString();
+ }
+}
+/**
+ * Returns the physical line at the given index (i.e., with delimiters and the gap).
+ * <p>
+ *
+ * @param index the line index
+ * @return the physical line
+ */
+String getPhysicalLine(int index) {
+ int start = lines[index][0];
+ int length = lines[index][1];
+ return getPhysicalText(start, length);
+}
+/**
+ * @return the number of lines in the text store
+ */
+public int getLineCount(){
+ return lineCount;
+}
+/**
+ * Returns the line at the given offset.
+ * <p>
+ *
+ * @param charPosition logical character offset (i.e., does not include gap)
+ * @return the line index
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when charPosition is out of range</li>
+ * </ul>
+ */
+public int getLineAtOffset(int charPosition){
+ int position;
+ if ((charPosition > getCharCount()) || (charPosition < 0)) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (charPosition < gapStart) {
+ // position is before the gap
+ position = charPosition;
+ } else {
+ // position includes the gap
+ position = charPosition + (gapEnd - gapStart);
+ }
+
+ // if last line and the line is not empty you can ask for
+ // a position that doesn't exist (the one to the right of the
+ // last character) - for inserting
+ if (lineCount > 0) {
+ int lastLine = lineCount - 1;
+ if (position == lines[lastLine][0] + lines[lastLine][1])
+ return lastLine;
+ }
+
+ int high = lineCount;
+ int low = -1;
+ int index = lineCount;
+ while (high - low > 1) {
+ index = (high + low) / 2;
+ int lineStart = lines[index][0];
+ int lineEnd = lineStart + lines[index][1] - 1;
+ if (position <= lineStart) {
+ high = index;
+ } else if (position <= lineEnd) {
+ high = index;
+ break;
+ } else {
+ low = index;
+ }
+ }
+
+ return high;
+}
+/**
+ * Returns the line index at the given physical offset.
+ * <p>
+ *
+ * @param position physical character offset (i.e., includes gap)
+ * @return the line index
+ */
+int getLineAtPhysicalOffset(int position){
+ int high = lineCount;
+ int low = -1;
+ int index = lineCount;
+ while (high - low > 1) {
+ index = (high + low) / 2;
+ int lineStart = lines[index][0];
+ int lineEnd = lineStart + lines[index][1] - 1;
+ if (position <= lineStart) {
+ high = index;
+ } else if (position <= lineEnd) {
+ high = index;
+ break;
+ } else {
+ low = index;
+ }
+ }
+ return high;
+}
+/**
+ * Returns the logical offset of the given line.
+ * <p>
+ *
+ * @param lineIndex index of line
+ * @return the logical starting offset of the line. When there are not any lines,
+ * getOffsetAtLine(0) is a valid call that should answer 0.
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when lineIndex is out of range</li>
+ * </ul>
+ */
+public int getOffsetAtLine(int lineIndex) {
+ if (lineIndex == 0) return 0;
+ if ((lineIndex >= lineCount) || (lineIndex < 0)) error(SWT.ERROR_INVALID_ARGUMENT);
+ int start = lines[lineIndex][0];
+ if (start > gapEnd) {
+ return start - (gapEnd - gapStart);
+ } else {
+ return start;
+ }
+}
+/**
+ * Increases the line indexes array to accomodate more lines.
+ * <p>
+ *
+ * @param numLines the number to increase the array by
+ */
+void expandLinesBy(int numLines) {
+ int size = lines.length;
+ if (size - lineCount >= numLines) {
+ return;
+ }
+ int[][] newLines = new int[size+Math.max(10, numLines)][2];
+ System.arraycopy(lines, 0, newLines, 0, size);
+ lines = newLines;
+}
+/**
+ * Reports an SWT error.
+ * <p>
+ *
+ * @param code the error code
+ */
+void error (int code) {
+ SWT.error(code);
+}
+/**
+ * Returns whether or not a gap exists in the text store.
+ * <p>
+ *
+ * @return true if gap exists, false otherwise
+ */
+boolean gapExists() {
+ return gapStart != gapEnd;
+}
+
+/**
+ * Returns a string representing the continous content of
+ * the text store.
+ * <p>
+ *
+ * @param start the physical start offset of the text to return
+ * @param length the physical length of the text to return
+ * @return the text
+ */
+String getPhysicalText(int start, int length) {
+ return new String(textStore, start, length);
+}
+/**
+ * Returns a string representing the logical content of
+ * the text store (i.e., gap stripped out).
+ * <p>
+ *
+ * @param start the logical start offset of the text to return
+ * @param length the logical length of the text to return
+ * @return the text
+ */
+public String getTextRange(int start, int length) {
+ if (textStore == null)
+ return "";
+ if (length == 0)
+ return "";
+ int end= start + length;
+ if (!gapExists() || (end < gapStart))
+ return new String(textStore, start, length);
+ if (gapStart < start) {
+ int gapLength= gapEnd - gapStart;
+ return new String(textStore, start + gapLength , length);
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append(textStore, start, gapStart - start);
+ buf.append(textStore, gapEnd, end - gapStart);
+ return buf.toString();
+}
+/**
+ * Removes the specified <code>TextChangeListener</code>.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeTextChangeListener(TextChangeListener listener){
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ for (int i=0; i<textListeners.size(); i++) {
+ TypedListener typedListener = (TypedListener) textListeners.elementAt(i);
+ if (typedListener.getEventListener () == listener) {
+ textListeners.removeElementAt(i);
+ break;
+ }
+ }
+}
+/**
+ * Replaces the text with <code>newText</code> starting at position <code>start</code>
+ * for a length of <code>replaceLength</code>. Notifies the appropriate listeners.
+ * <p>
+ *
+ * When sending the TextChangingEvent, <code>newLineCount</code> is the number of
+ * lines that are going to be inserted and <code>replaceLineCount</code> is
+ * the number of lines that are going to be deleted, based on the change
+ * that occurs visually. For example:
+ * <ul>
+ * <li>(replaceText,newText) ==> (replaceLineCount,newLineCount)
+ * <li>("","\n") ==> (0,1)
+ * <li>("\n\n","a") ==> (2,0)
+ * </ul>
+ * </p>
+ *
+ * @param start start offset of text to replace
+ * @param replaceLength start offset of text to replace
+ * @param newText start offset of text to replace
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when the text change results in a multi byte
+ * line delimiter being split or partially deleted. Splitting a line
+ * delimiter by inserting text between the CR and LF characters of the
+ * \r\n delimiter or deleting part of this line delimiter is not supported</li>
+ * </ul>
+ */
+public void replaceTextRange(int start, int replaceLength, String newText){
+ // check for invalid replace operations
+ if (!isValidReplace(start, replaceLength, newText)) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+
+ // inform listeners
+ StyledTextEvent event = new StyledTextEvent(this);
+ event.type = StyledText.TextChanging;
+ event.start = start;
+ event.replaceLineCount = lineCount(start, replaceLength);
+ event.text = newText;
+ event.newLineCount = lineCount(newText);
+ event.replaceCharCount = replaceLength;
+ event.newCharCount = newText.length();
+ sendTextEvent(event);
+
+ // first delete the text to be replaced
+ delete(start, replaceLength, event.replaceLineCount + 1);
+ // then insert the new text
+ insert(start, newText);
+ // inform listeners
+ event = new StyledTextEvent(this);
+ event.type = StyledText.TextChanged;
+ sendTextEvent(event);
+ // printLines();
+}
+/**
+ * Sends the text listeners the TextChanged event.
+ */
+void sendTextEvent(StyledTextEvent event) {
+ for (int i=0; i<textListeners.size(); i++) {
+ ((StyledTextListener)textListeners.elementAt(i)).handleEvent(event);
+ }
+}
+/**
+ * Sets the content to text and removes the gap since there are no sensible predictions
+ * about where the next change will occur.
+ * <p>
+ *
+ * @param text the text
+ */
+public void setText (String text){
+ textStore = text.toCharArray();
+ gapStart = -1;
+ gapEnd = -1;
+ expandExp = 1;
+ indexLines();
+ StyledTextEvent event = new StyledTextEvent(this);
+ event.type = StyledText.TextSet;
+ event.text = "";
+ sendTextEvent(event);
+}
+/**
+ * Deletes text.
+ * <p>
+ * @param position the position at which the text to delete starts
+ * @param length the length of the text to delete
+ * @param numLines the number of lines that are being deleted
+ */
+void delete(int position, int length, int numLines) {
+ if (length == 0) return;
+
+ int startLine = getLineAtOffset(position);
+ int startLineOffset = getOffsetAtLine(startLine);
+ int endLine = getLineAtOffset(position + length);
+
+ String endText = "";
+ boolean splittingDelimiter = false;
+ if (position + length < getCharCount()) {
+ endText = getTextRange(position + length - 1, 2);
+ if ((endText.charAt(0) == SWT.CR) && (endText.charAt(1) == SWT.LF)) {
+ splittingDelimiter = true;
+ }
+ }
+
+ adjustGap(position + length, -length, startLine);
+ int [][] oldLines = indexLines(position, length + (gapEnd - gapStart), numLines);
+
+ // enlarge the gap - the gap can be enlarged either to the
+ // right or left
+ if (position + length == gapStart) {
+ gapStart -= length;
+ } else {
+ gapEnd += length;
+ }
+
+ // figure out the length of the new concatenated line, do so by
+ // finding the first line delmiter after position
+ int j = position;
+ boolean eol = false;
+ while (j < textStore.length && !eol) {
+ if (j < gapStart || j >= gapEnd) {
+ char ch = textStore[j];
+ if (isDelimiter(ch)) {
+ if (j + 1 < textStore.length)
+ if (ch == SWT.CR && (textStore[j+1] == SWT.LF))
+ j++;
+ eol = true;
+ }
+ }
+ j++;
+ }
+ // update the line where the deletion started
+ lines[startLine][1] = (position - startLineOffset) + (j - position);
+ // figure out the number of lines that have been deleted
+ int numOldLines = oldLines.length - 1;
+ if (splittingDelimiter) numOldLines -= 1;
+ // shift up the lines after the last deleted line, no need to update
+ // the offset or length of the lines
+ for (int i = endLine + 1; i < lineCount; i++) {
+ lines[i - numOldLines]=lines[i];
+ }
+ lineCount -= numOldLines;
+ gapLine = getLineAtPhysicalOffset(gapStart);
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java
index 19224bd3cf..962386b47f 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/DefaultLineStyler.java
@@ -1,681 +1,681 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.internal.Compatibility;
-import java.util.Vector;
-
-class DefaultLineStyler implements LineStyleListener, LineBackgroundListener {
- StyledTextContent content;
- StyleRange styles[] = new StyleRange[0];
- int styleCount = 0; // the number of styles
- int styleExpandExp = 1; // the expansion exponent, used to increase the styles array exponentially
- int lineExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially
- int lineCount = 0;
- Color lineBackgrounds[];
-
-/**
- * Creates a new default line styler.
- * <p>
- *
- * @param content the text to which the styles apply
- */
-public DefaultLineStyler(StyledTextContent content) {
- this.content = content;
- lineCount = content.getLineCount();
- lineBackgrounds = new Color[lineCount];
-}
-/**
- * Inserts a style at the given location.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to insert the style (the new style
- * will reside at this index)
- *
- */
-void insertStyle(StyleRange style, int index) {
- insertStyles(new StyleRange[] {style}, index);
-}
-/**
- * Insert the styles at the given location.
- * <p>
- *
- * @param insertStyles the new styles
- * @param index the index at which to insert the styles (the first new style
- * will reside at this index)
- *
- */
-void insertStyles(StyleRange[] insertStyles, int index) {
- int size = styles.length;
- int insertCount = insertStyles.length;
- int spaceNeeded = styleCount + insertCount - size;
- if (spaceNeeded > 0) {
- StyleRange[] newStyles = new StyleRange[size+spaceNeeded];
- System.arraycopy(styles, 0, newStyles, 0, size);
- styles = newStyles;
- }
- // shift the styles down to make room for the new styles
- System.arraycopy(styles, index, styles, index + insertCount, styleCount - index);
- // add the new styles
- System.arraycopy(insertStyles, 0, styles, index, insertCount);
- styleCount = styleCount + insertCount;
-}
-/**
- * Inserts a style, merging it with adjacent styles if possible.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to insert the style (the new style
- * will reside at this index)
- * @return true if the style was inserted, false if the style was merged with an adjacent
- * style
- */
-boolean insertMergeStyle(StyleRange style, int index) {
- if (mergeStyleBefore(style, index)) return false;
- if (mergeStyleAfter(style, index)) return false;
- insertStyle(style, index);
- return true;
-}
-/**
- * Merges the style with the style before it if possible.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to attempt the merge.
- * @return true if the style was merged, false otherwise
- */
-boolean mergeStyleBefore(StyleRange style, int index) {
- // see if the style is similar to the style before it and merge the
- // styles if possible
- if (index > 0) {
- StyleRange previous = styles[index-1];
- if (style.similarTo(previous)) {
- // the start of style needs to be in the range of the previous style
- // and the end of style needs to be < the start of the next style
- int previousEnd = previous.start + previous.length;
- if ((style.start <= previousEnd) && (style.start >= previous.start)) {
- int styleEnd = style.start + style.length;
- if ((index == styleCount) || (styleEnd <= styles[index].start)) {
- previous.length = style.start + style.length - previous.start;
- return true;
- }
- }
- }
- }
- return false;
-}
-/**
- * Merges the style with the style after it if possible.
- * <p>
- *
- * @param style the new style
- * @param index the index at which to attempt the merge.
- * @return true if the style was merged, false otherwise
- */
-boolean mergeStyleAfter(StyleRange style, int index) {
- // see if the style is similar to the style that will be after it and
- // merge the styles if possible
- if (index < styleCount) {
- StyleRange next = styles[index];
- if (style.similarTo(next)) {
- // the end of style needs to be in the range of the next style and
- // the start of style needs to be > the end of the previous style
- int styleEnd = style.start + style.length;
- int nextEnd = next.start + next.length;
- if ((styleEnd <= nextEnd) && (styleEnd >= next.start)) {
- if ((index == 0) || (style.start >= styles[index-1].start + styles[index-1].length)) {
- next.length = next.start + next.length - style.start;
- next.start = style.start;
- return true;
- }
- }
- }
- }
- return false;
-}
-/**
- * Removes style information that is defined for the range of text in <code>clearStyle</code>.
- * <p>
- *
- * @param clearStyle the style information to use for clearing
- */
-void clearStyle(StyleRange clearStyle) {
- Point pt = getOverlappingStyles(clearStyle.start, clearStyle.length);
- int clearStyleEnd = clearStyle.start + clearStyle.length - 1;
-
- // no overlapped styles exist
- if ((pt == null) || (pt.y == 0)) return;
-
- // the newStyle overlaps one or more of the existing styles
- // pt.x is the index of the first overlapped style, pt.y is the number of overlapped
- // styles
- int count = 0;
- int deleteStyle = -1;
- int deleteCount = 0;
- for (int i=pt.x; count<pt.y; i++) {
- StyleRange overlap = styles[i];
- int overlapEnd = overlap.start + overlap.length - 1;
- if (overlap.start < clearStyle.start) {
- if (overlapEnd <= clearStyleEnd) {
- // the end of overlap needs to be cleared
- overlap.length=clearStyle.start - overlap.start;
- } else {
- // middle of overlap needs to be cleared, this will
- // cause overlap to be broken into two
- StyleRange endStyle = (StyleRange)overlap.clone();
- endStyle.start = clearStyleEnd + 1;
- endStyle.length = overlapEnd - clearStyleEnd;
- overlap.length = clearStyle.start - overlap.start;
- insertStyle(endStyle, i+1);
- break;
- }
- } else {
- if (overlapEnd <= clearStyleEnd) {
- // entire overlap needs to be cleared
- if (deleteStyle == -1) {
- deleteStyle = i;
- }
- deleteCount++;
- } else {
- // beginning of overlap needs to be cleared
- overlap.start=clearStyleEnd + 1;
- overlap.length=overlapEnd - overlap.start + 1;
- break;
- }
- }
- count++;
- }
- deleteStyles(deleteStyle, deleteCount);
-}
-/**
- * Increases the <code>linebackgrounds</code> array to accomodate new line background
- * information.
- * <p>
- *
- * @param numLines the number to increase the array by
- */
-void expandLinesBy(int numLines) {
- int size = lineBackgrounds.length;
- if (size - lineCount >= numLines) {
- return;
- }
- Color[] newLines = new Color[size+Math.max(Compatibility.pow2(lineExpandExp), numLines)];
- System.arraycopy(lineBackgrounds, 0, newLines, 0, size);
- lineBackgrounds = newLines;
- lineExpandExp++;
-}
-/**
- * Deletes the style at <code>index</code>.
- * <p>
- *
- * @param index the index of the style to be deleted
- */
-void deleteStyle(int index) {
- deleteStyles(index, 1);
-}
-/**
- * Delete count styles starting at <code>index</code>.
- * <p>
- *
- * @param index the index of the style to be deleted
- * @param count the number of styles to be deleted
- */
-void deleteStyles(int index, int count) {
- if ((count == 0) || (index < 0)) return;
- // shift the styles up
- System.arraycopy(styles, index + count, styles, index, styleCount - (index + count));
- for (int i=0; i<count; i++) {
- styles[styleCount - i - 1] = null;
- }
- styleCount = styleCount - count;
-}
-/**
- * Returns the styles that are defined.
- * <p>
- *
- * @return the copied array of styles
- */
-StyleRange [] getStyleRanges() {
- StyleRange[] newStyles = new StyleRange[styleCount];
- System.arraycopy(styles, 0, newStyles, 0, styleCount);
- return newStyles;
-}
-/**
- * Handles the get line background color callback.
- * <p>
- *
- * @param event.lineOffset line number (input)
- * @param event.lineText line text (input)
- * @param event.background line background color (output)
- */
-public void lineGetBackground(LineBackgroundEvent event) {
- int lineIndex = content.getLineAtOffset(event.lineOffset);
- event.lineBackground = lineBackgrounds[lineIndex];
-}
-/**
- * Handles the get line style information callback.
- * <p>
- *
- * @param event.lineOffset line number (input)
- * @param event.lineText line text (input)
- * @param event.styles array of StyleRanges, need to be in order (output)
- */
-public void lineGetStyle(LineStyleEvent event) {
- int lineStart = event.lineOffset;
- int lineEnd = lineStart + event.lineText.length();
-
- int high = searchForStyle(lineStart, lineEnd);
- StyleRange style = null;
- Vector lineStyles = new Vector();
-
- // index will represent a style that
- // -- starts after the line (end processing)
- // -- ends before the line (continue processing)
- // -- starts before the line, ends in the line (add range)
- // -- starts in the line, ends in the line (add range)
- // -- starts in the line, ends after the line (add range)
- // -- starts before the line, ends after the line (add range)
- for (int index = high; index < styleCount; index++) {
- style = styles[index];
- if (style.start > lineEnd)
- // style starts after the line, end looping
- break;
- int styleEnd = style.start + style.length - 1;
- if (styleEnd >= lineStart) lineStyles.addElement(style);
- }
- event.styles = new StyleRange[lineStyles.size()];
- lineStyles.copyInto(event.styles);
-}
-/**
- * Searches for the first style in the <code>start</code> - <code>end</code> range.
- * <p>
- *
- * @return the index of the first style that overlaps the input range
- */
-int searchForStyle(int start, int end) {
- int high = styleCount;
- int low = -1;
- int index = high;
- // find the index of the first style for the given range, use a binary search
- while (high - low > 1) {
- index = (high + low) / 2;
- StyleRange style = styles[index];
- int styleEnd = style.start + style.length - 1;
- if (start <= style.start || end <= styleEnd || (start > style.start && styleEnd >= start && styleEnd < end)) {
- high = index;
- }
- else {
- low = index;
- }
- }
- return high;
-}
-/**
- * Updates the line background colors to reflect a new color. Called by StyledText.
- * <p>
- *
- * @param startLine index of the first line to color
- * @param lineCount number of lines to color starting at startLine
- * @param background the background color for the lines
- */
-void setLineBackground(int startLine, int count, Color background) {
- for (int i=startLine; i<startLine + count; i++) {
- lineBackgrounds[i]=background;
- }
-}
-/**
- * Update the styles to reflect the new style. <code>newStyle</code> will
- * replace any old style for the range. When this method is called, the
- * DefaultLineStyler may merge the new style with an existing style (if possible).
- * Called by StyledText when a style is added. Called by StyledText.
- * <p>
- *
- * @param newStyle the new style information.
- */
-void setStyleRange(StyleRange newStyle) {
- if (newStyle == null) {
- styles = new StyleRange[0];
- styleExpandExp = 1;
- styleCount = 0;
- return;
- }
- if (newStyle.length ==0) return;
- if (newStyle.isUnstyled()) {
- clearStyle(newStyle);
- return;
- }
-
- Point pt = getOverlappingStyles(newStyle.start, newStyle.length);
- int newStyleEnd = newStyle.start + newStyle.length - 1;
-
- // no styles exist
- if (pt == null) {
- insertStyle(newStyle, 0);
- return;
- }
-
- // newStyle does not overlap any other styles
- if (pt.y == 0) {
- insertMergeStyle(newStyle, pt.x);
- return;
- }
-
- // the newStyle overlaps one or more of the existing styles
- boolean added = false; // indicates whether or not the new style has been added
- int count = 0;
- // pt.x is the index of the first overlapped style, pt.y is the number of overlapped
- // styles
- for (int i=pt.x; count<pt.y; i++) {
- StyleRange overlap = styles[i];
- int overlapEnd = overlap.start + overlap.length - 1;
- if (overlap.start < newStyle.start) {
- if (overlapEnd <= newStyleEnd) {
- // the end of overlap needs to be replaced by newStyle
- if (newStyle.similarTo(overlap)) {
- // update overlap to accomodate the new style
- overlap.length = newStyle.start + newStyle.length - overlap.start;
- } else {
- overlap.length=newStyle.start - overlap.start;
- // see if newStyle can be merged with the style after overlap, if so,
- // processing is done
- if (mergeStyleAfter(newStyle, i+1)) break;
- // otherwise, insert the newStyle, newStyle may still overlap other
- // styles after it so continue processing
- insertStyle(newStyle, i+1);
- i++;
- }
- added = true;
- } else {
- // middle of overlap needs to be replaced by newStyle, this will
- // cause overlap to be broken into two
- if (newStyle.similarTo(overlap)) break;
- StyleRange endStyle = (StyleRange)overlap.clone();
- endStyle.start = newStyleEnd + 1;
- endStyle.length = overlapEnd - newStyleEnd;
- overlap.length = newStyle.start - overlap.start;
- insertStyle(newStyle, i+1);
- i++;
- insertStyle(endStyle, i+1);
- // when newStyle overlaps the middle of a style, this implies that
- // processing is done (no more overlapped styles)
- break;
- }
- } else {
- if (overlapEnd <= newStyleEnd) {
- // overlap will be replaced by the newStyle, make sure newStyle
- // hasn't already been added, if it has just delete overlap
- if (!added) {
- styles[i] = newStyle;
- added = true;
- } else {
- deleteStyle(i);
- i--;
- }
- } else {
- // beginning of overlap needs to be replaced by newStyle
- overlap.start=newStyleEnd + 1;
- overlap.length=overlapEnd - overlap.start + 1;
- if (!added) {
- insertMergeStyle(newStyle, i);
- }
- // when newStyle overlaps only the beginning of a style, this implies
- // that processing is done (no more overlapped styles)
- break;
- }
- }
- count++;
- }
-}
-/**
- * Replace the styles for the given range.
- * <p>
- *
- * @param styles the new styles, must be in order and non-overlapping
- */
-void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
- clearStyle(new StyleRange(start, length, null, null));
- // find insert point
- int high = styleCount;
- int low = -1;
- int index = high;
- while (high - low > 1) {
- index = (high + low) / 2;
- StyleRange style = styles[index];
- if (start <= style.start) {
- high = index;
- }
- else {
- low = index;
- }
- }
- insertStyles(ranges, high);
-}
-/**
- * Sets the array of styles and discards old styles. Called by StyledText.
- * <p>
- *
- * @param styles the new styles, must be in order and non-overlapping
- */
-void setStyleRanges(StyleRange[] styles) {
- this.styles = new StyleRange[styles.length];
- System.arraycopy(styles, 0, this.styles, 0, styles.length);
- styleCount = styles.length;
- styleExpandExp = 1;
-}
-/**
- * Updates the style ranges and line backgrounds to reflect a pending text
- * change.
- * Called by StyledText when a TextChangingEvent is received.
- * <p>
- *
- * @param event the event with the text change information
- */
-public void textChanging(TextChangingEvent event) {
- int startLine = content.getLineAtOffset(event.start);
- int startLineOffset = content.getOffsetAtLine(startLine);
-
- textChanging(event.start, -event.replaceCharCount);
- textChanging(event.start, event.newCharCount);
-
- if (event.replaceCharCount == content.getCharCount()) {
- // all text is going to be replaced, clear line backgrounds
- linesChanging(0, -lineCount);
- linesChanging(0, content.getLineCount() - event.replaceLineCount + event.newLineCount);
- return;
- }
-
- if (event.start != startLineOffset) {
- startLine = startLine + 1;
- }
-
- linesChanging(startLine, -event.replaceLineCount);
- linesChanging(startLine, event.newLineCount);
-}
-/*
- * Updates the line backgrounds to reflect a pending text change.
- * <p>
- *
- * @param start the starting line of the change that is about to take place
- * @param delta the number of lines in the change, > 0 indicates lines inserted,
- * < 0 indicates lines deleted
- */
-void linesChanging(int start, int delta) {
- if (delta == 0) return;
- boolean inserting = delta > 0;
- if (inserting) {
- // shift the lines down to make room for new lines
- expandLinesBy(delta);
- for (int i = lineCount-1; i >= start; i--) {
- lineBackgrounds[i + delta]=lineBackgrounds[i];
- }
- for (int i=start; i<start + delta; i++) {
- lineBackgrounds[i]=null;
- }
- } else {
- // shift up the lines
- for (int i = start - delta; i < lineCount; i++) {
- lineBackgrounds[i+delta]=lineBackgrounds[i];
- }
- }
- lineCount += delta;
-}
-/*
- * Updates the style ranges to reflect a text change.
- * <p>
- *
- * @param start the starting offset of the change that is about to
- * take place
- * @param delta the length of the change, > 0 indicates text inserted,
- * < 0 indicates text deleted
- */
-void textChanging(int start, int delta) {
- if (delta == 0) return;
- StyleRange style;
- // find the index of the first style for the given offset, use a binary search
- // to find the index
- int end;
- int deleteStart = -1;
- int deleteCount = 0;
- boolean inserting = delta > 0;
- if (inserting) {
- end = (start + delta) - 1;
- } else {
- end = (start - delta) - 1;
- }
- int high = searchForStyle(start, end);
- int index;
- // update the styles that are in the affected range
- for (index = high; index < styleCount; index++) {
- style = styles[index];
- if (inserting) {
- if (style.start >= start) break;
- // in the insert case only one style range will be directly affected,
- // it will need to be split into two and then the newStyle inserted
- StyleRange beforeStyle = (StyleRange)style.clone();
- beforeStyle.length = start - style.start;
- style.start = start;
- style.length = style.length - beforeStyle.length;
- if (beforeStyle.length != 0) insertStyle(beforeStyle, index);
- index++;
- break;
- } else {
- int styleEnd = style.start + style.length - 1;
- if (style.start > end) break;
- // in the delete case, any style that overlaps the change range will be
- // affected
- if (style.start < start) {
- if (styleEnd <= end) {
- // style starts before change range, ends in change range
- style.length = start - style.start;
- } else {
- // style starts before change range, ends after change range
- style.length = style.length + delta;
- index++;
- break;
- }
- } else {
- if (styleEnd <= end) {
- // style starts in change range, ends in change range
- if (deleteStart == -1) {
- deleteStart = index;
- }
- deleteCount++;
- } else {
- // style starts in change range, ends after change range
- style.start = start;
- style.length = styleEnd - end;
- index++;
- break;
- }
- }
- }
- }
- deleteStyles(deleteStart, deleteCount);
- // change the offsets of the styles after the affected styles
- for (int i = index - deleteCount; i < styleCount; i++) {
- style = styles[i];
- style.start = style.start + delta;
- }
-}
-/**
- * Returns the indexes of the styles that overlap the given range. Styles that partially
- * or fully overlap the range will be returned.
- * <p>
- *
- * @return Point where x is the index of the starting overlap style, y is the number of
- * styles that overlap the range
- */
-Point getOverlappingStyles(int start, int length) {
- StyleRange style;
- if (styleCount == 0) return null;
- // find the index of the first style for the given offset, use a binary search
- // to find the index
- int end = start + length - 1;
- int high = searchForStyle(start, end);
- int count = 0;
- for (int index = high; index < styleCount; index++) {
- style = styles[index];
- int styleEnd = style.start + style.length - 1;
- if (style.start > end) break;
- if (styleEnd >= start) count++;
- }
- return new Point(high, count);
-}
-/**
- * Returns the background color of a line. Called by StyledText. It is safe to return
- * the existing Color object since the colors are set and managed by the client.
- * <p>
- *
- * @param index the line index
- * @return the background color of the line at the given index
- */
-Color getLineBackground(int index) {
- return lineBackgrounds[index];
-}
-/**
- * Returns the style for the character at <code>offset</code>. Called by StyledText.
- * Returns a new style. Does not return the existing style.
- * <p>
- *
- * @param offset the character position in the text
- * @return a cloned StyleRange with start == offset and length == 1 if a style is
- * specified or null if no style is specified
- */
-StyleRange getStyleRangeAtOffset(int offset) {
- if (styleCount == 0) return null;
- Point pt = getOverlappingStyles(offset, 1);
- if (pt == null || pt.y == 0) return null;
- StyleRange newStyle = (StyleRange)styles[pt.x].clone();
- newStyle.start = offset;
- newStyle.length = 1;
- return newStyle;
-}
-/**
- * Returns the styles for the given range. Returns the existing styles,
- * so be careful not to modify the return value. Styles are not cloned
- * in order to make this method as efficient as possible.
- * <p>
- *
- * @param offset the start position of the text range
- * @param length the length of the text range
- * @return a StyleRange array or null if no styles are specified for the text
- * range
- */
-StyleRange[] getStyleRangesFor(int offset, int length) {
- if (styleCount == 0) return null;
- Point pt = getOverlappingStyles(offset, length);
- if (pt == null || pt.y == 0) return null;
- StyleRange[] ranges = new StyleRange[pt.y];
- for (int i=0; i<pt.y; i++) {
- StyleRange newStyle = styles[pt.x + i];
- ranges[i]=newStyle;
- }
- return ranges;
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.Compatibility;
+import java.util.Vector;
+
+class DefaultLineStyler implements LineStyleListener, LineBackgroundListener {
+ StyledTextContent content;
+ StyleRange styles[] = new StyleRange[0];
+ int styleCount = 0; // the number of styles
+ int styleExpandExp = 1; // the expansion exponent, used to increase the styles array exponentially
+ int lineExpandExp = 1; // the expansion exponent, used to increase the lines array exponentially
+ int lineCount = 0;
+ Color lineBackgrounds[];
+
+/**
+ * Creates a new default line styler.
+ * <p>
+ *
+ * @param content the text to which the styles apply
+ */
+public DefaultLineStyler(StyledTextContent content) {
+ this.content = content;
+ lineCount = content.getLineCount();
+ lineBackgrounds = new Color[lineCount];
+}
+/**
+ * Inserts a style at the given location.
+ * <p>
+ *
+ * @param style the new style
+ * @param index the index at which to insert the style (the new style
+ * will reside at this index)
+ *
+ */
+void insertStyle(StyleRange style, int index) {
+ insertStyles(new StyleRange[] {style}, index);
+}
+/**
+ * Insert the styles at the given location.
+ * <p>
+ *
+ * @param insertStyles the new styles
+ * @param index the index at which to insert the styles (the first new style
+ * will reside at this index)
+ *
+ */
+void insertStyles(StyleRange[] insertStyles, int index) {
+ int size = styles.length;
+ int insertCount = insertStyles.length;
+ int spaceNeeded = styleCount + insertCount - size;
+ if (spaceNeeded > 0) {
+ StyleRange[] newStyles = new StyleRange[size+spaceNeeded];
+ System.arraycopy(styles, 0, newStyles, 0, size);
+ styles = newStyles;
+ }
+ // shift the styles down to make room for the new styles
+ System.arraycopy(styles, index, styles, index + insertCount, styleCount - index);
+ // add the new styles
+ System.arraycopy(insertStyles, 0, styles, index, insertCount);
+ styleCount = styleCount + insertCount;
+}
+/**
+ * Inserts a style, merging it with adjacent styles if possible.
+ * <p>
+ *
+ * @param style the new style
+ * @param index the index at which to insert the style (the new style
+ * will reside at this index)
+ * @return true if the style was inserted, false if the style was merged with an adjacent
+ * style
+ */
+boolean insertMergeStyle(StyleRange style, int index) {
+ if (mergeStyleBefore(style, index)) return false;
+ if (mergeStyleAfter(style, index)) return false;
+ insertStyle(style, index);
+ return true;
+}
+/**
+ * Merges the style with the style before it if possible.
+ * <p>
+ *
+ * @param style the new style
+ * @param index the index at which to attempt the merge.
+ * @return true if the style was merged, false otherwise
+ */
+boolean mergeStyleBefore(StyleRange style, int index) {
+ // see if the style is similar to the style before it and merge the
+ // styles if possible
+ if (index > 0) {
+ StyleRange previous = styles[index-1];
+ if (style.similarTo(previous)) {
+ // the start of style needs to be in the range of the previous style
+ // and the end of style needs to be < the start of the next style
+ int previousEnd = previous.start + previous.length;
+ if ((style.start <= previousEnd) && (style.start >= previous.start)) {
+ int styleEnd = style.start + style.length;
+ if ((index == styleCount) || (styleEnd <= styles[index].start)) {
+ previous.length = style.start + style.length - previous.start;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+/**
+ * Merges the style with the style after it if possible.
+ * <p>
+ *
+ * @param style the new style
+ * @param index the index at which to attempt the merge.
+ * @return true if the style was merged, false otherwise
+ */
+boolean mergeStyleAfter(StyleRange style, int index) {
+ // see if the style is similar to the style that will be after it and
+ // merge the styles if possible
+ if (index < styleCount) {
+ StyleRange next = styles[index];
+ if (style.similarTo(next)) {
+ // the end of style needs to be in the range of the next style and
+ // the start of style needs to be > the end of the previous style
+ int styleEnd = style.start + style.length;
+ int nextEnd = next.start + next.length;
+ if ((styleEnd <= nextEnd) && (styleEnd >= next.start)) {
+ if ((index == 0) || (style.start >= styles[index-1].start + styles[index-1].length)) {
+ next.length = next.start + next.length - style.start;
+ next.start = style.start;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+/**
+ * Removes style information that is defined for the range of text in <code>clearStyle</code>.
+ * <p>
+ *
+ * @param clearStyle the style information to use for clearing
+ */
+void clearStyle(StyleRange clearStyle) {
+ Point pt = getOverlappingStyles(clearStyle.start, clearStyle.length);
+ int clearStyleEnd = clearStyle.start + clearStyle.length - 1;
+
+ // no overlapped styles exist
+ if ((pt == null) || (pt.y == 0)) return;
+
+ // the newStyle overlaps one or more of the existing styles
+ // pt.x is the index of the first overlapped style, pt.y is the number of overlapped
+ // styles
+ int count = 0;
+ int deleteStyle = -1;
+ int deleteCount = 0;
+ for (int i=pt.x; count<pt.y; i++) {
+ StyleRange overlap = styles[i];
+ int overlapEnd = overlap.start + overlap.length - 1;
+ if (overlap.start < clearStyle.start) {
+ if (overlapEnd <= clearStyleEnd) {
+ // the end of overlap needs to be cleared
+ overlap.length=clearStyle.start - overlap.start;
+ } else {
+ // middle of overlap needs to be cleared, this will
+ // cause overlap to be broken into two
+ StyleRange endStyle = (StyleRange)overlap.clone();
+ endStyle.start = clearStyleEnd + 1;
+ endStyle.length = overlapEnd - clearStyleEnd;
+ overlap.length = clearStyle.start - overlap.start;
+ insertStyle(endStyle, i+1);
+ break;
+ }
+ } else {
+ if (overlapEnd <= clearStyleEnd) {
+ // entire overlap needs to be cleared
+ if (deleteStyle == -1) {
+ deleteStyle = i;
+ }
+ deleteCount++;
+ } else {
+ // beginning of overlap needs to be cleared
+ overlap.start=clearStyleEnd + 1;
+ overlap.length=overlapEnd - overlap.start + 1;
+ break;
+ }
+ }
+ count++;
+ }
+ deleteStyles(deleteStyle, deleteCount);
+}
+/**
+ * Increases the <code>linebackgrounds</code> array to accomodate new line background
+ * information.
+ * <p>
+ *
+ * @param numLines the number to increase the array by
+ */
+void expandLinesBy(int numLines) {
+ int size = lineBackgrounds.length;
+ if (size - lineCount >= numLines) {
+ return;
+ }
+ Color[] newLines = new Color[size+Math.max(Compatibility.pow2(lineExpandExp), numLines)];
+ System.arraycopy(lineBackgrounds, 0, newLines, 0, size);
+ lineBackgrounds = newLines;
+ lineExpandExp++;
+}
+/**
+ * Deletes the style at <code>index</code>.
+ * <p>
+ *
+ * @param index the index of the style to be deleted
+ */
+void deleteStyle(int index) {
+ deleteStyles(index, 1);
+}
+/**
+ * Delete count styles starting at <code>index</code>.
+ * <p>
+ *
+ * @param index the index of the style to be deleted
+ * @param count the number of styles to be deleted
+ */
+void deleteStyles(int index, int count) {
+ if ((count == 0) || (index < 0)) return;
+ // shift the styles up
+ System.arraycopy(styles, index + count, styles, index, styleCount - (index + count));
+ for (int i=0; i<count; i++) {
+ styles[styleCount - i - 1] = null;
+ }
+ styleCount = styleCount - count;
+}
+/**
+ * Returns the styles that are defined.
+ * <p>
+ *
+ * @return the copied array of styles
+ */
+StyleRange [] getStyleRanges() {
+ StyleRange[] newStyles = new StyleRange[styleCount];
+ System.arraycopy(styles, 0, newStyles, 0, styleCount);
+ return newStyles;
+}
+/**
+ * Handles the get line background color callback.
+ * <p>
+ *
+ * @param event.lineOffset line number (input)
+ * @param event.lineText line text (input)
+ * @param event.background line background color (output)
+ */
+public void lineGetBackground(LineBackgroundEvent event) {
+ int lineIndex = content.getLineAtOffset(event.lineOffset);
+ event.lineBackground = lineBackgrounds[lineIndex];
+}
+/**
+ * Handles the get line style information callback.
+ * <p>
+ *
+ * @param event.lineOffset line number (input)
+ * @param event.lineText line text (input)
+ * @param event.styles array of StyleRanges, need to be in order (output)
+ */
+public void lineGetStyle(LineStyleEvent event) {
+ int lineStart = event.lineOffset;
+ int lineEnd = lineStart + event.lineText.length();
+
+ int high = searchForStyle(lineStart, lineEnd);
+ StyleRange style = null;
+ Vector lineStyles = new Vector();
+
+ // index will represent a style that
+ // -- starts after the line (end processing)
+ // -- ends before the line (continue processing)
+ // -- starts before the line, ends in the line (add range)
+ // -- starts in the line, ends in the line (add range)
+ // -- starts in the line, ends after the line (add range)
+ // -- starts before the line, ends after the line (add range)
+ for (int index = high; index < styleCount; index++) {
+ style = styles[index];
+ if (style.start > lineEnd)
+ // style starts after the line, end looping
+ break;
+ int styleEnd = style.start + style.length - 1;
+ if (styleEnd >= lineStart) lineStyles.addElement(style);
+ }
+ event.styles = new StyleRange[lineStyles.size()];
+ lineStyles.copyInto(event.styles);
+}
+/**
+ * Searches for the first style in the <code>start</code> - <code>end</code> range.
+ * <p>
+ *
+ * @return the index of the first style that overlaps the input range
+ */
+int searchForStyle(int start, int end) {
+ int high = styleCount;
+ int low = -1;
+ int index = high;
+ // find the index of the first style for the given range, use a binary search
+ while (high - low > 1) {
+ index = (high + low) / 2;
+ StyleRange style = styles[index];
+ int styleEnd = style.start + style.length - 1;
+ if (start <= style.start || end <= styleEnd || (start > style.start && styleEnd >= start && styleEnd < end)) {
+ high = index;
+ }
+ else {
+ low = index;
+ }
+ }
+ return high;
+}
+/**
+ * Updates the line background colors to reflect a new color. Called by StyledText.
+ * <p>
+ *
+ * @param startLine index of the first line to color
+ * @param lineCount number of lines to color starting at startLine
+ * @param background the background color for the lines
+ */
+void setLineBackground(int startLine, int count, Color background) {
+ for (int i=startLine; i<startLine + count; i++) {
+ lineBackgrounds[i]=background;
+ }
+}
+/**
+ * Update the styles to reflect the new style. <code>newStyle</code> will
+ * replace any old style for the range. When this method is called, the
+ * DefaultLineStyler may merge the new style with an existing style (if possible).
+ * Called by StyledText when a style is added. Called by StyledText.
+ * <p>
+ *
+ * @param newStyle the new style information.
+ */
+void setStyleRange(StyleRange newStyle) {
+ if (newStyle == null) {
+ styles = new StyleRange[0];
+ styleExpandExp = 1;
+ styleCount = 0;
+ return;
+ }
+ if (newStyle.length ==0) return;
+ if (newStyle.isUnstyled()) {
+ clearStyle(newStyle);
+ return;
+ }
+
+ Point pt = getOverlappingStyles(newStyle.start, newStyle.length);
+ int newStyleEnd = newStyle.start + newStyle.length - 1;
+
+ // no styles exist
+ if (pt == null) {
+ insertStyle(newStyle, 0);
+ return;
+ }
+
+ // newStyle does not overlap any other styles
+ if (pt.y == 0) {
+ insertMergeStyle(newStyle, pt.x);
+ return;
+ }
+
+ // the newStyle overlaps one or more of the existing styles
+ boolean added = false; // indicates whether or not the new style has been added
+ int count = 0;
+ // pt.x is the index of the first overlapped style, pt.y is the number of overlapped
+ // styles
+ for (int i=pt.x; count<pt.y; i++) {
+ StyleRange overlap = styles[i];
+ int overlapEnd = overlap.start + overlap.length - 1;
+ if (overlap.start < newStyle.start) {
+ if (overlapEnd <= newStyleEnd) {
+ // the end of overlap needs to be replaced by newStyle
+ if (newStyle.similarTo(overlap)) {
+ // update overlap to accomodate the new style
+ overlap.length = newStyle.start + newStyle.length - overlap.start;
+ } else {
+ overlap.length=newStyle.start - overlap.start;
+ // see if newStyle can be merged with the style after overlap, if so,
+ // processing is done
+ if (mergeStyleAfter(newStyle, i+1)) break;
+ // otherwise, insert the newStyle, newStyle may still overlap other
+ // styles after it so continue processing
+ insertStyle(newStyle, i+1);
+ i++;
+ }
+ added = true;
+ } else {
+ // middle of overlap needs to be replaced by newStyle, this will
+ // cause overlap to be broken into two
+ if (newStyle.similarTo(overlap)) break;
+ StyleRange endStyle = (StyleRange)overlap.clone();
+ endStyle.start = newStyleEnd + 1;
+ endStyle.length = overlapEnd - newStyleEnd;
+ overlap.length = newStyle.start - overlap.start;
+ insertStyle(newStyle, i+1);
+ i++;
+ insertStyle(endStyle, i+1);
+ // when newStyle overlaps the middle of a style, this implies that
+ // processing is done (no more overlapped styles)
+ break;
+ }
+ } else {
+ if (overlapEnd <= newStyleEnd) {
+ // overlap will be replaced by the newStyle, make sure newStyle
+ // hasn't already been added, if it has just delete overlap
+ if (!added) {
+ styles[i] = newStyle;
+ added = true;
+ } else {
+ deleteStyle(i);
+ i--;
+ }
+ } else {
+ // beginning of overlap needs to be replaced by newStyle
+ overlap.start=newStyleEnd + 1;
+ overlap.length=overlapEnd - overlap.start + 1;
+ if (!added) {
+ insertMergeStyle(newStyle, i);
+ }
+ // when newStyle overlaps only the beginning of a style, this implies
+ // that processing is done (no more overlapped styles)
+ break;
+ }
+ }
+ count++;
+ }
+}
+/**
+ * Replace the styles for the given range.
+ * <p>
+ *
+ * @param styles the new styles, must be in order and non-overlapping
+ */
+void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
+ clearStyle(new StyleRange(start, length, null, null));
+ // find insert point
+ int high = styleCount;
+ int low = -1;
+ int index = high;
+ while (high - low > 1) {
+ index = (high + low) / 2;
+ StyleRange style = styles[index];
+ if (start <= style.start) {
+ high = index;
+ }
+ else {
+ low = index;
+ }
+ }
+ insertStyles(ranges, high);
+}
+/**
+ * Sets the array of styles and discards old styles. Called by StyledText.
+ * <p>
+ *
+ * @param styles the new styles, must be in order and non-overlapping
+ */
+void setStyleRanges(StyleRange[] styles) {
+ this.styles = new StyleRange[styles.length];
+ System.arraycopy(styles, 0, this.styles, 0, styles.length);
+ styleCount = styles.length;
+ styleExpandExp = 1;
+}
+/**
+ * Updates the style ranges and line backgrounds to reflect a pending text
+ * change.
+ * Called by StyledText when a TextChangingEvent is received.
+ * <p>
+ *
+ * @param event the event with the text change information
+ */
+public void textChanging(TextChangingEvent event) {
+ int startLine = content.getLineAtOffset(event.start);
+ int startLineOffset = content.getOffsetAtLine(startLine);
+
+ textChanging(event.start, -event.replaceCharCount);
+ textChanging(event.start, event.newCharCount);
+
+ if (event.replaceCharCount == content.getCharCount()) {
+ // all text is going to be replaced, clear line backgrounds
+ linesChanging(0, -lineCount);
+ linesChanging(0, content.getLineCount() - event.replaceLineCount + event.newLineCount);
+ return;
+ }
+
+ if (event.start != startLineOffset) {
+ startLine = startLine + 1;
+ }
+
+ linesChanging(startLine, -event.replaceLineCount);
+ linesChanging(startLine, event.newLineCount);
+}
+/*
+ * Updates the line backgrounds to reflect a pending text change.
+ * <p>
+ *
+ * @param start the starting line of the change that is about to take place
+ * @param delta the number of lines in the change, > 0 indicates lines inserted,
+ * < 0 indicates lines deleted
+ */
+void linesChanging(int start, int delta) {
+ if (delta == 0) return;
+ boolean inserting = delta > 0;
+ if (inserting) {
+ // shift the lines down to make room for new lines
+ expandLinesBy(delta);
+ for (int i = lineCount-1; i >= start; i--) {
+ lineBackgrounds[i + delta]=lineBackgrounds[i];
+ }
+ for (int i=start; i<start + delta; i++) {
+ lineBackgrounds[i]=null;
+ }
+ } else {
+ // shift up the lines
+ for (int i = start - delta; i < lineCount; i++) {
+ lineBackgrounds[i+delta]=lineBackgrounds[i];
+ }
+ }
+ lineCount += delta;
+}
+/*
+ * Updates the style ranges to reflect a text change.
+ * <p>
+ *
+ * @param start the starting offset of the change that is about to
+ * take place
+ * @param delta the length of the change, > 0 indicates text inserted,
+ * < 0 indicates text deleted
+ */
+void textChanging(int start, int delta) {
+ if (delta == 0) return;
+ StyleRange style;
+ // find the index of the first style for the given offset, use a binary search
+ // to find the index
+ int end;
+ int deleteStart = -1;
+ int deleteCount = 0;
+ boolean inserting = delta > 0;
+ if (inserting) {
+ end = (start + delta) - 1;
+ } else {
+ end = (start - delta) - 1;
+ }
+ int high = searchForStyle(start, end);
+ int index;
+ // update the styles that are in the affected range
+ for (index = high; index < styleCount; index++) {
+ style = styles[index];
+ if (inserting) {
+ if (style.start >= start) break;
+ // in the insert case only one style range will be directly affected,
+ // it will need to be split into two and then the newStyle inserted
+ StyleRange beforeStyle = (StyleRange)style.clone();
+ beforeStyle.length = start - style.start;
+ style.start = start;
+ style.length = style.length - beforeStyle.length;
+ if (beforeStyle.length != 0) insertStyle(beforeStyle, index);
+ index++;
+ break;
+ } else {
+ int styleEnd = style.start + style.length - 1;
+ if (style.start > end) break;
+ // in the delete case, any style that overlaps the change range will be
+ // affected
+ if (style.start < start) {
+ if (styleEnd <= end) {
+ // style starts before change range, ends in change range
+ style.length = start - style.start;
+ } else {
+ // style starts before change range, ends after change range
+ style.length = style.length + delta;
+ index++;
+ break;
+ }
+ } else {
+ if (styleEnd <= end) {
+ // style starts in change range, ends in change range
+ if (deleteStart == -1) {
+ deleteStart = index;
+ }
+ deleteCount++;
+ } else {
+ // style starts in change range, ends after change range
+ style.start = start;
+ style.length = styleEnd - end;
+ index++;
+ break;
+ }
+ }
+ }
+ }
+ deleteStyles(deleteStart, deleteCount);
+ // change the offsets of the styles after the affected styles
+ for (int i = index - deleteCount; i < styleCount; i++) {
+ style = styles[i];
+ style.start = style.start + delta;
+ }
+}
+/**
+ * Returns the indexes of the styles that overlap the given range. Styles that partially
+ * or fully overlap the range will be returned.
+ * <p>
+ *
+ * @return Point where x is the index of the starting overlap style, y is the number of
+ * styles that overlap the range
+ */
+Point getOverlappingStyles(int start, int length) {
+ StyleRange style;
+ if (styleCount == 0) return null;
+ // find the index of the first style for the given offset, use a binary search
+ // to find the index
+ int end = start + length - 1;
+ int high = searchForStyle(start, end);
+ int count = 0;
+ for (int index = high; index < styleCount; index++) {
+ style = styles[index];
+ int styleEnd = style.start + style.length - 1;
+ if (style.start > end) break;
+ if (styleEnd >= start) count++;
+ }
+ return new Point(high, count);
+}
+/**
+ * Returns the background color of a line. Called by StyledText. It is safe to return
+ * the existing Color object since the colors are set and managed by the client.
+ * <p>
+ *
+ * @param index the line index
+ * @return the background color of the line at the given index
+ */
+Color getLineBackground(int index) {
+ return lineBackgrounds[index];
+}
+/**
+ * Returns the style for the character at <code>offset</code>. Called by StyledText.
+ * Returns a new style. Does not return the existing style.
+ * <p>
+ *
+ * @param offset the character position in the text
+ * @return a cloned StyleRange with start == offset and length == 1 if a style is
+ * specified or null if no style is specified
+ */
+StyleRange getStyleRangeAtOffset(int offset) {
+ if (styleCount == 0) return null;
+ Point pt = getOverlappingStyles(offset, 1);
+ if (pt == null || pt.y == 0) return null;
+ StyleRange newStyle = (StyleRange)styles[pt.x].clone();
+ newStyle.start = offset;
+ newStyle.length = 1;
+ return newStyle;
+}
+/**
+ * Returns the styles for the given range. Returns the existing styles,
+ * so be careful not to modify the return value. Styles are not cloned
+ * in order to make this method as efficient as possible.
+ * <p>
+ *
+ * @param offset the start position of the text range
+ * @param length the length of the text range
+ * @return a StyleRange array or null if no styles are specified for the text
+ * range
+ */
+StyleRange[] getStyleRangesFor(int offset, int length) {
+ if (styleCount == 0) return null;
+ Point pt = getOverlappingStyles(offset, length);
+ if (pt == null || pt.y == 0) return null;
+ StyleRange[] ranges = new StyleRange[pt.y];
+ for (int i=0; i<pt.y; i++) {
+ StyleRange newStyle = styles[pt.x + i];
+ ranges[i]=newStyle;
+ }
+ return ranges;
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyEvent.java
index 6a0b40c2dd..8bda363356 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyEvent.java
@@ -1,25 +1,25 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-
-/**
- * This event is sent after a text change occurs.
- */
-public final class ExtendedModifyEvent extends TypedEvent {
- public int start; // start offset of the new text
- public int length; // length of the new text
- public String replacedText; // replaced text or empty string if no text was replaced
-
-public ExtendedModifyEvent(StyledTextEvent e) {
- super(e);
- start = e.start;
- length = e.end - e.start;
- replacedText = e.text;
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+
+/**
+ * This event is sent after a text change occurs.
+ */
+public final class ExtendedModifyEvent extends TypedEvent {
+ public int start; // start offset of the new text
+ public int length; // length of the new text
+ public String replacedText; // replaced text or empty string if no text was replaced
+
+public ExtendedModifyEvent(StyledTextEvent e) {
+ super(e);
+ start = e.start;
+ length = e.end - e.start;
+ replacedText = e.text;
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyListener.java
index a365c831ff..1c15e0c4b3 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ExtendedModifyListener.java
@@ -1,23 +1,23 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.internal.SWTEventListener;
-
-public interface ExtendedModifyListener extends SWTEventListener {
-/**
- * This method is called after a text change occurs.
- * <p>
- *
- * @param event.start the start offset of the new text (input)
- * @param event.length the length of the new text (input)
- * @param event.replacedText the replaced text (input)
- */
-public void modifyText(ExtendedModifyEvent event);
-}
-
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.internal.SWTEventListener;
+
+public interface ExtendedModifyListener extends SWTEventListener {
+/**
+ * This method is called after a text change occurs.
+ * <p>
+ *
+ * @param event.start the start offset of the new text (input)
+ * @param event.length the length of the new text (input)
+ * @param event.replacedText the replaced text (input)
+ */
+public void modifyText(ExtendedModifyEvent event);
+}
+
+
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundEvent.java
index 916f265a81..5c050eeb4f 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundEvent.java
@@ -1,27 +1,27 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.graphics.*;
-
-/**
- * This event is sent when a line is about to be drawn.
- */
-public class LineBackgroundEvent extends TypedEvent {
- public int lineOffset; // line start offset
- public String lineText; // line text
- public Color lineBackground; // line background color
-
-public LineBackgroundEvent(StyledTextEvent e) {
- super(e);
- lineOffset = e.detail;
- lineText = e.text;
-}
-}
-
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * This event is sent when a line is about to be drawn.
+ */
+public class LineBackgroundEvent extends TypedEvent {
+ public int lineOffset; // line start offset
+ public String lineText; // line text
+ public Color lineBackground; // line background color
+
+public LineBackgroundEvent(StyledTextEvent e) {
+ super(e);
+ lineOffset = e.detail;
+ lineText = e.text;
+}
+}
+
+
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundListener.java
index 14103cf4c2..bb00d05b1a 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineBackgroundListener.java
@@ -1,23 +1,23 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.internal.SWTEventListener;
-
-public interface LineBackgroundListener extends SWTEventListener {
-
-/**
- * This method is called when a line is about to be drawn in order to get its
- * background color.
- * <p>
- *
- * @param event.lineOffset line start offset (input)
- * @param event.lineText line text (input)
- * @param event.lineBackground line background color (output)
- */
-public void lineGetBackground(LineBackgroundEvent event);
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.internal.SWTEventListener;
+
+public interface LineBackgroundListener extends SWTEventListener {
+
+/**
+ * This method is called when a line is about to be drawn in order to get its
+ * background color.
+ * <p>
+ *
+ * @param event.lineOffset line start offset (input)
+ * @param event.lineText line text (input)
+ * @param event.lineBackground line background color (output)
+ */
+public void lineGetBackground(LineBackgroundEvent event);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleEvent.java
index 74d7fa5bf0..cf65387bb0 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleEvent.java
@@ -1,25 +1,25 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-
-/**
- * This event is sent when a line is about to be drawn.
- */
-public class LineStyleEvent extends TypedEvent {
- public int lineOffset; // line start offset
- public String lineText; // line text
- public StyleRange[] styles; // array of StyleRanges
-
-public LineStyleEvent(StyledTextEvent e) {
- super(e);
- lineOffset = e.detail;
- lineText = e.text;
- styles = e.styles;
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+
+/**
+ * This event is sent when a line is about to be drawn.
+ */
+public class LineStyleEvent extends TypedEvent {
+ public int lineOffset; // line start offset
+ public String lineText; // line text
+ public StyleRange[] styles; // array of StyleRanges
+
+public LineStyleEvent(StyledTextEvent e) {
+ super(e);
+ lineOffset = e.detail;
+ lineText = e.text;
+ styles = e.styles;
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleListener.java
index 9ade47d9f0..0d172e7b6e 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/LineStyleListener.java
@@ -1,22 +1,22 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.internal.SWTEventListener;
-
-public interface LineStyleListener extends SWTEventListener {
-/**
- * This method is called when a line is about to be drawn in order to get the
- * line's style information.
- * <p>
- *
- * @param event.lineOffset line start offset (input)
- * @param event.lineText line text (input)
- * @param event.styles array of StyleRanges, need to be in order (output)
- */
-public void lineGetStyle(LineStyleEvent event);
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.internal.SWTEventListener;
+
+public interface LineStyleListener extends SWTEventListener {
+/**
+ * This method is called when a line is about to be drawn in order to get the
+ * line's style information.
+ * <p>
+ *
+ * @param event.lineOffset line start offset (input)
+ * @param event.lineText line text (input)
+ * @param event.styles array of StyleRanges, need to be in order (output)
+ */
+public void lineGetStyle(LineStyleEvent event);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/PopupList.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/PopupList.java
index 43ab4c30ec..9a22421a9f 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/PopupList.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/PopupList.java
@@ -1,250 +1,250 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-import org.eclipse.swt.*;
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-/**
-* A PopupList is a list of selectable items that appears in its own shell positioned above
-* its parent shell. It it used for selecting items when editing a Table cell (similar to the
-* list that appears when you open a Combo box).
-*
-* The list will be positioned so that does not run off the screen and the largest number of items
-* are visible. It may appear above the current cursor location or below it depending how close you
-* are to the edge of the screen.
-*/
-public class PopupList {
- private Shell shell;
- private List list;
- private int minimumWidth;
-/**
-* Creates a PopupList above the specified shell.
-*/
-public PopupList(Shell parent) {
-
- shell = new Shell(parent, 0);
-
- list = new List(shell, SWT.SINGLE | SWT.V_SCROLL);
-
- // close dialog if user selects outside of the shell
- shell.addListener(SWT.Deactivate, new Listener() {
- public void handleEvent(Event e){
- shell.setVisible (false);
- };
- });
-
- // resize shell when list resizes
- shell.addControlListener(new ControlListener() {
- public void controlMoved(ControlEvent e){}
- public void controlResized(ControlEvent e){
- Rectangle shellSize = shell.getClientArea();
- list.setSize(shellSize.width, shellSize.height);
- }
- });
-
- // return list selection on Mouse Up or Carriage Return
- list.addMouseListener(new MouseListener() {
- public void mouseDoubleClick(MouseEvent e){};
- public void mouseDown(MouseEvent e){};
- public void mouseUp(MouseEvent e){
- shell.setVisible (false);
- };
- });
- list.addKeyListener(new KeyListener() {
- public void keyReleased(KeyEvent e){};
- public void keyPressed(KeyEvent e){
- if (e.character == '\r'){
- shell.setVisible (false);
- }
- };
- });
-
-}
-/**
-* Gets the widget font.
-* <p>
-* @return the widget font
-*
-* @exception SWTError <ul>
-* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
-* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
-* </ul>
-*/
-public Font getFont () {
- return list.getFont();
-}
-/**
-* Gets the items.
-* <p>
-* This operation will fail if the items cannot
-* be queried from the OS.
-*
-* @return the items in the widget
-*
-* @exception SWTError <ul>
-* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
-* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
-* <li>ERROR_CANNOT_GET_ITEM when the operation fails</li>
-* </ul>
-*/
-public String[] getItems () {
- return list.getItems();
-}
-/**
-* Gets the minimum width of the list.
-*
-* @return the minimum width of the list
-*/
-public int getMinimumWidth () {
- return minimumWidth;
-}
-/**
-* Launches the Popup List, waits for an item to be selected and then closes PopupList.
-*
-* @param rect the initial size and location of the PopupList; the dialog will be
-* positioned so that it does not run off the screen and the largest number of items are visible
-*
-* @return the text of the selected item or null if no item is selected
-*/
-public String open (Rectangle rect) {
-
- Point listSize = list.computeSize (rect.width, SWT.DEFAULT);
- Rectangle screenSize = shell.getDisplay().getBounds();
-
- // Position the dialog so that it does not run off the screen and the largest number of items are visible
- int spaceBelow = screenSize.height - (rect.y + rect.height) - 30;
- int spaceAbove = rect.y - 30;
-
- int y = 0;
- if (spaceAbove > spaceBelow && listSize.y > spaceBelow) {
- // place popup list above table cell
- if (listSize.y > spaceAbove){
- listSize.y = spaceAbove;
- } else {
- listSize.y += 2;
- }
- y = rect.y - listSize.y;
-
- } else {
- // place popup list below table cell
- if (listSize.y > spaceBelow){
- listSize.y = spaceBelow;
- } else {
- listSize.y += 2;
- }
- y = rect.y + rect.height;
- }
-
- // Make dialog as wide as the cell
- listSize.x = rect.width;
- // dialog width should not be les than minimumwidth
- if (listSize.x < minimumWidth)
- listSize.x = minimumWidth;
-
- // Align right side of dialog with right side of cell
- int x = rect.x + rect.width - listSize.x;
-
- shell.setBounds(x, y, listSize.x, listSize.y);
-
- shell.open();
- list.setFocus();
-
- Display display = shell.getDisplay();
- while (!shell.isDisposed () && shell.isVisible ()) {
- if (!display.readAndDispatch()) display.sleep();
- }
-
- String result = null;
- if (!shell.isDisposed ()) {
- String [] strings = list.getSelection ();
- shell.dispose();
- if (strings.length != 0) result = strings [0];
- }
- return result;
-}
-/**
-* Selects an item with text that starts with specified String.
-* <p>
-* If the item is not currently selected, it is selected.
-* If the item at an index is selected, it remains selected.
-* If the string is not matched, it is ignored.
-*
-* @param string the text of the item
-*
-* @exception SWTError <ul>
-* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
-* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
-* </ul>
-*/
-public void select(String string) {
- String[] items = list.getItems();
-
- // find the first entry in the list that starts with the
- // specified string
- if (string != null){
- for (int i = 0; i < items.length; i++) {
- if (items[i].startsWith(string)){
- int index = list.indexOf(items[i]);
- list.select(index);
- break;
- }
- }
- }
-}
-/**
-* Sets the widget font.
-* <p>
-* When new font is null, the font reverts
-* to the default system font for the widget.
-*
-* @param font the new font (or null)
-*
-* @exception SWTError <ul>
-* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
-* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
-* </ul>
-*/
-public void setFont (Font font) {
- list.setFont(font);
-}
-/**
-* Sets all items.
-* <p>
-* The previous selection is cleared.
-* The previous items are deleted.
-* The new items are added.
-* The top index is set to 0.
-*
-* @param items the array of items
-*
-* This operation will fail when an item is null
-* or could not be added in the OS.
-*
-* @exception SWTError <ul>
-* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
-* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
-* <li>ERROR_NULL_ARGUMENT when items is null</li>
-* <li>ERROR_ITEM_NOT_ADDED when the operation fails</li>
-* </ul>
-*/
-public void setItems (String[] strings) {
- list.setItems(strings);
-}
-/**
-* Sets the minimum width of the list.
-*
-* @param width the minimum width of the list
-*/
-public void setMinimumWidth (int width) {
- if (width < 0)
- throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
-
- minimumWidth = width;
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+/**
+* A PopupList is a list of selectable items that appears in its own shell positioned above
+* its parent shell. It it used for selecting items when editing a Table cell (similar to the
+* list that appears when you open a Combo box).
+*
+* The list will be positioned so that does not run off the screen and the largest number of items
+* are visible. It may appear above the current cursor location or below it depending how close you
+* are to the edge of the screen.
+*/
+public class PopupList {
+ private Shell shell;
+ private List list;
+ private int minimumWidth;
+/**
+* Creates a PopupList above the specified shell.
+*/
+public PopupList(Shell parent) {
+
+ shell = new Shell(parent, 0);
+
+ list = new List(shell, SWT.SINGLE | SWT.V_SCROLL);
+
+ // close dialog if user selects outside of the shell
+ shell.addListener(SWT.Deactivate, new Listener() {
+ public void handleEvent(Event e){
+ shell.setVisible (false);
+ };
+ });
+
+ // resize shell when list resizes
+ shell.addControlListener(new ControlListener() {
+ public void controlMoved(ControlEvent e){}
+ public void controlResized(ControlEvent e){
+ Rectangle shellSize = shell.getClientArea();
+ list.setSize(shellSize.width, shellSize.height);
+ }
+ });
+
+ // return list selection on Mouse Up or Carriage Return
+ list.addMouseListener(new MouseListener() {
+ public void mouseDoubleClick(MouseEvent e){};
+ public void mouseDown(MouseEvent e){};
+ public void mouseUp(MouseEvent e){
+ shell.setVisible (false);
+ };
+ });
+ list.addKeyListener(new KeyListener() {
+ public void keyReleased(KeyEvent e){};
+ public void keyPressed(KeyEvent e){
+ if (e.character == '\r'){
+ shell.setVisible (false);
+ }
+ };
+ });
+
+}
+/**
+* Gets the widget font.
+* <p>
+* @return the widget font
+*
+* @exception SWTError <ul>
+* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+* </ul>
+*/
+public Font getFont () {
+ return list.getFont();
+}
+/**
+* Gets the items.
+* <p>
+* This operation will fail if the items cannot
+* be queried from the OS.
+*
+* @return the items in the widget
+*
+* @exception SWTError <ul>
+* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+* <li>ERROR_CANNOT_GET_ITEM when the operation fails</li>
+* </ul>
+*/
+public String[] getItems () {
+ return list.getItems();
+}
+/**
+* Gets the minimum width of the list.
+*
+* @return the minimum width of the list
+*/
+public int getMinimumWidth () {
+ return minimumWidth;
+}
+/**
+* Launches the Popup List, waits for an item to be selected and then closes PopupList.
+*
+* @param rect the initial size and location of the PopupList; the dialog will be
+* positioned so that it does not run off the screen and the largest number of items are visible
+*
+* @return the text of the selected item or null if no item is selected
+*/
+public String open (Rectangle rect) {
+
+ Point listSize = list.computeSize (rect.width, SWT.DEFAULT);
+ Rectangle screenSize = shell.getDisplay().getBounds();
+
+ // Position the dialog so that it does not run off the screen and the largest number of items are visible
+ int spaceBelow = screenSize.height - (rect.y + rect.height) - 30;
+ int spaceAbove = rect.y - 30;
+
+ int y = 0;
+ if (spaceAbove > spaceBelow && listSize.y > spaceBelow) {
+ // place popup list above table cell
+ if (listSize.y > spaceAbove){
+ listSize.y = spaceAbove;
+ } else {
+ listSize.y += 2;
+ }
+ y = rect.y - listSize.y;
+
+ } else {
+ // place popup list below table cell
+ if (listSize.y > spaceBelow){
+ listSize.y = spaceBelow;
+ } else {
+ listSize.y += 2;
+ }
+ y = rect.y + rect.height;
+ }
+
+ // Make dialog as wide as the cell
+ listSize.x = rect.width;
+ // dialog width should not be les than minimumwidth
+ if (listSize.x < minimumWidth)
+ listSize.x = minimumWidth;
+
+ // Align right side of dialog with right side of cell
+ int x = rect.x + rect.width - listSize.x;
+
+ shell.setBounds(x, y, listSize.x, listSize.y);
+
+ shell.open();
+ list.setFocus();
+
+ Display display = shell.getDisplay();
+ while (!shell.isDisposed () && shell.isVisible ()) {
+ if (!display.readAndDispatch()) display.sleep();
+ }
+
+ String result = null;
+ if (!shell.isDisposed ()) {
+ String [] strings = list.getSelection ();
+ shell.dispose();
+ if (strings.length != 0) result = strings [0];
+ }
+ return result;
+}
+/**
+* Selects an item with text that starts with specified String.
+* <p>
+* If the item is not currently selected, it is selected.
+* If the item at an index is selected, it remains selected.
+* If the string is not matched, it is ignored.
+*
+* @param string the text of the item
+*
+* @exception SWTError <ul>
+* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+* </ul>
+*/
+public void select(String string) {
+ String[] items = list.getItems();
+
+ // find the first entry in the list that starts with the
+ // specified string
+ if (string != null){
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].startsWith(string)){
+ int index = list.indexOf(items[i]);
+ list.select(index);
+ break;
+ }
+ }
+ }
+}
+/**
+* Sets the widget font.
+* <p>
+* When new font is null, the font reverts
+* to the default system font for the widget.
+*
+* @param font the new font (or null)
+*
+* @exception SWTError <ul>
+* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+* </ul>
+*/
+public void setFont (Font font) {
+ list.setFont(font);
+}
+/**
+* Sets all items.
+* <p>
+* The previous selection is cleared.
+* The previous items are deleted.
+* The new items are added.
+* The top index is set to 0.
+*
+* @param items the array of items
+*
+* This operation will fail when an item is null
+* or could not be added in the OS.
+*
+* @exception SWTError <ul>
+* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+* <li>ERROR_NULL_ARGUMENT when items is null</li>
+* <li>ERROR_ITEM_NOT_ADDED when the operation fails</li>
+* </ul>
+*/
+public void setItems (String[] strings) {
+ list.setItems(strings);
+}
+/**
+* Sets the minimum width of the list.
+*
+* @param width the minimum width of the list
+*/
+public void setMinimumWidth (int width) {
+ if (width < 0)
+ throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
+
+ minimumWidth = width;
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ST.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ST.java
index e445f0e40e..8ec93cc273 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ST.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ST.java
@@ -1,62 +1,62 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-
-/**
- * This class provides access to the public constants provided by <code>StyledText</code>.
- */
-public class ST {
-
- /* StyledText key action constants. Key bindings for the actions are set
- * by the StyledText widget. @see StyledText#createKeyBindings()
- */
-
- /* Navigation Key Actions */
- public static final int LINE_UP = 16777217; // binding = SWT.ARROW_UP
- public static final int LINE_DOWN = 16777218; // binding = SWT.ARROW_DOWN
- public static final int LINE_START = 16777223; // binding = SWT.HOME
- public static final int LINE_END = 16777224; // binding = SWT.END
- public static final int COLUMN_PREVIOUS = 16777219; // binding = SWT.ARROW_LEFT
- public static final int COLUMN_NEXT = 16777220; // binding = SWT.ARROW_RIGHT
- public static final int PAGE_UP = 16777221; // binding = SWT.PAGE_UP
- public static final int PAGE_DOWN = 16777222; // binding = SWT.PAGE_DOWN
- public static final int WORD_PREVIOUS = 17039363; // binding = SWT.MOD1 + SWT.ARROW_LEFT
- public static final int WORD_NEXT = 17039364; // binding = SWT.MOD1 + SWT.ARROW_RIGHT
- public static final int TEXT_START = 17039367; // binding = SWT.MOD1 + SWT.HOME
- public static final int TEXT_END = 17039368; // binding = SWT.MOD1 + SWT.END
- public static final int WINDOW_START = 17039365; // binding = SWT.MOD1 + SWT.PAGE_UP
- public static final int WINDOW_END = 17039366; // binding = SWT.MOD1 + SWT.PAGE_DOWN
-
- /* Selection Key Actions */
- public static final int SELECT_LINE_UP = 16908289; // binding = SWT.MOD2 + SWT.ARROW_UP
- public static final int SELECT_LINE_DOWN = 16908290; // binding = SWT.MOD2 + SWT.ARROW_DOWN
- public static final int SELECT_LINE_START = 16908295; // binding = SWT.MOD2 + SWT.HOME
- public static final int SELECT_LINE_END = 16908296; // binding = SWT.MOD2 + SWT.END
- public static final int SELECT_COLUMN_PREVIOUS = 16908291; // binding = SWT.MOD2 + SWT.ARROW_LEFT
- public static final int SELECT_COLUMN_NEXT = 16908292; // binding = SWT.MOD2 + SWT.ARROW_RIGHT
- public static final int SELECT_PAGE_UP = 16908293; // binding = SWT.MOD2 + SWT.PAGE_UP
- public static final int SELECT_PAGE_DOWN = 16908294; // binding = SWT.MOD2 + SWT.PAGE_DOWN
- public static final int SELECT_WORD_PREVIOUS = 17170435; // binding = SWT.MOD1 + SWT.MOD2 + SWT.ARROW_LEFT
- public static final int SELECT_WORD_NEXT = 17170436; // binding = SWT.MOD1 + SWT.MOD2 + SWT.ARROW_RIGHT
- public static final int SELECT_TEXT_START = 17170439; // binding = SWT.MOD1 + SWT.MOD2 + SWT.HOME
- public static final int SELECT_TEXT_END = 17170440; // binding = SWT.MOD1 + SWT.MOD2 + SWT.END
- public static final int SELECT_WINDOW_START = 17170437; // binding = SWT.MOD1 + SWT.MOD2 + SWT.PAGE_UP
- public static final int SELECT_WINDOW_END = 17170438; // binding = SWT.MOD1 + SWT.MOD2 + SWT.PAGE_DOWN
-
- /* Modification Key Actions */
- public static final int CUT = 131199; // binding = SWT.MOD2 + SWT.DEL
- public static final int COPY = 17039369; // binding = SWT.MOD1 + SWT.INSERT;
- public static final int PASTE = 16908297; // binding = SWT.MOD2 + SWT.INSERT ;
- public static final int DELETE_PREVIOUS = '\b'; // binding = SWT.BS;
- public static final int DELETE_NEXT = 0x7F; // binding = SWT.DEL;
- public static final int DELETE_WORD_PREVIOUS = 262152; // binding = SWT.BS | SWT.MOD1;
- public static final int DELETE_WORD_NEXT = 262271; // binding = SWT.DEL | SWT.MOD1;
-
- /* Miscellaneous Key Actions */
- public static final int TOGGLE_OVERWRITE = 16777225; // binding = SWT.INSERT;
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+
+/**
+ * This class provides access to the public constants provided by <code>StyledText</code>.
+ */
+public class ST {
+
+ /* StyledText key action constants. Key bindings for the actions are set
+ * by the StyledText widget. @see StyledText#createKeyBindings()
+ */
+
+ /* Navigation Key Actions */
+ public static final int LINE_UP = 16777217; // binding = SWT.ARROW_UP
+ public static final int LINE_DOWN = 16777218; // binding = SWT.ARROW_DOWN
+ public static final int LINE_START = 16777223; // binding = SWT.HOME
+ public static final int LINE_END = 16777224; // binding = SWT.END
+ public static final int COLUMN_PREVIOUS = 16777219; // binding = SWT.ARROW_LEFT
+ public static final int COLUMN_NEXT = 16777220; // binding = SWT.ARROW_RIGHT
+ public static final int PAGE_UP = 16777221; // binding = SWT.PAGE_UP
+ public static final int PAGE_DOWN = 16777222; // binding = SWT.PAGE_DOWN
+ public static final int WORD_PREVIOUS = 17039363; // binding = SWT.MOD1 + SWT.ARROW_LEFT
+ public static final int WORD_NEXT = 17039364; // binding = SWT.MOD1 + SWT.ARROW_RIGHT
+ public static final int TEXT_START = 17039367; // binding = SWT.MOD1 + SWT.HOME
+ public static final int TEXT_END = 17039368; // binding = SWT.MOD1 + SWT.END
+ public static final int WINDOW_START = 17039365; // binding = SWT.MOD1 + SWT.PAGE_UP
+ public static final int WINDOW_END = 17039366; // binding = SWT.MOD1 + SWT.PAGE_DOWN
+
+ /* Selection Key Actions */
+ public static final int SELECT_LINE_UP = 16908289; // binding = SWT.MOD2 + SWT.ARROW_UP
+ public static final int SELECT_LINE_DOWN = 16908290; // binding = SWT.MOD2 + SWT.ARROW_DOWN
+ public static final int SELECT_LINE_START = 16908295; // binding = SWT.MOD2 + SWT.HOME
+ public static final int SELECT_LINE_END = 16908296; // binding = SWT.MOD2 + SWT.END
+ public static final int SELECT_COLUMN_PREVIOUS = 16908291; // binding = SWT.MOD2 + SWT.ARROW_LEFT
+ public static final int SELECT_COLUMN_NEXT = 16908292; // binding = SWT.MOD2 + SWT.ARROW_RIGHT
+ public static final int SELECT_PAGE_UP = 16908293; // binding = SWT.MOD2 + SWT.PAGE_UP
+ public static final int SELECT_PAGE_DOWN = 16908294; // binding = SWT.MOD2 + SWT.PAGE_DOWN
+ public static final int SELECT_WORD_PREVIOUS = 17170435; // binding = SWT.MOD1 + SWT.MOD2 + SWT.ARROW_LEFT
+ public static final int SELECT_WORD_NEXT = 17170436; // binding = SWT.MOD1 + SWT.MOD2 + SWT.ARROW_RIGHT
+ public static final int SELECT_TEXT_START = 17170439; // binding = SWT.MOD1 + SWT.MOD2 + SWT.HOME
+ public static final int SELECT_TEXT_END = 17170440; // binding = SWT.MOD1 + SWT.MOD2 + SWT.END
+ public static final int SELECT_WINDOW_START = 17170437; // binding = SWT.MOD1 + SWT.MOD2 + SWT.PAGE_UP
+ public static final int SELECT_WINDOW_END = 17170438; // binding = SWT.MOD1 + SWT.MOD2 + SWT.PAGE_DOWN
+
+ /* Modification Key Actions */
+ public static final int CUT = 131199; // binding = SWT.MOD2 + SWT.DEL
+ public static final int COPY = 17039369; // binding = SWT.MOD1 + SWT.INSERT;
+ public static final int PASTE = 16908297; // binding = SWT.MOD2 + SWT.INSERT ;
+ public static final int DELETE_PREVIOUS = '\b'; // binding = SWT.BS;
+ public static final int DELETE_NEXT = 0x7F; // binding = SWT.DEL;
+ public static final int DELETE_WORD_PREVIOUS = 262152; // binding = SWT.BS | SWT.MOD1;
+ public static final int DELETE_WORD_NEXT = 262271; // binding = SWT.DEL | SWT.MOD1;
+
+ /* Miscellaneous Key Actions */
+ public static final int TOGGLE_OVERWRITE = 16777225; // binding = SWT.INSERT;
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java
index f39fec4d10..e9df0ca766 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java
@@ -1,450 +1,450 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.graphics.*;
-
-/**
- * The SashForm lays out its children in a Row or Column arrangement (as specified
- * by the orientation) and places a Sash between the children.
- * One child may be maximized to occupy the entire size of the SashForm.
- * The relative sizes of the children may be specfied using weights.
- *
- * <p>
- * <dl>
- * <dt><b>Styles:</b><dd>HORIZONTAL, VERTICAL
- * </dl>
- */
-public class SashForm extends Composite {
-
- public int SASH_WIDTH = 3;
-
- private static final int DRAG_MINIMUM = 20;
-
- private int orientation = SWT.HORIZONTAL;
- private Sash[] sashes = new Sash[0];
- private Control[] controls = new Control[0];
- private Control maxControl = null;
- private Listener sashListener;
- private final static String LAYOUT_RATIO = "layout ratio"; //$NON-NLS-1$
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#HORIZONTAL
- * @see SWT#VERTICAL
- * @see #getStyle
- */
-public SashForm(Composite parent, int style) {
- super(parent, checkStyle(style));
- if ((style & SWT.VERTICAL) != 0){
- orientation = SWT.VERTICAL;
- }
-
- this.addListener(SWT.Resize, new Listener() {
- public void handleEvent(Event e) {
- layout(true);
- }
- });
-
- sashListener = new Listener() {
- public void handleEvent(Event e) {
- onDragSash(e);
- }
- };
-}
-private static int checkStyle (int style) {
- int mask = SWT.BORDER;
- return style & mask;
-}
-public Point computeSize (int wHint, int hHint, boolean changed) {
- checkWidget();
- Control[] controls = getControls(true);
- if (controls.length == 0) return new Point(wHint, hHint);
-
- int width = 0;
- int height = 0;
- boolean vertical = (orientation == SWT.VERTICAL);
- if (vertical) {
- height += (controls.length - 1) * SASH_WIDTH;
- } else {
- width += (controls.length - 1) * SASH_WIDTH;
- }
- for (int i = 0; i < controls.length; i++) {
- if (vertical) {
- Point size = controls[i].computeSize(wHint, SWT.DEFAULT);
- height += size.y;
- width = Math.max(width, size.x);
- } else {
- Point size = controls[i].computeSize(SWT.DEFAULT, hHint);
- width += size.x;
- height = Math.max(height, size.y);
- }
- }
- if (wHint != SWT.DEFAULT) width = wHint;
- if (hHint != SWT.DEFAULT) height = hHint;
-
- return new Point(width, height);
-}
-/**
- * Returns SWT.HORIZONTAL if the controls in the SashForm are laid out side by side
- * or SWT.VERTICAL if the controls in the SashForm are laid out top to bottom.
- *
- * @return SWT.HORIZONTAL or SWT.VERTICAL
- */
-public int getOrientation() {
- //checkWidget();
- return orientation;
-}
-/**
- * Answer the control that currently is maximized in the SashForm.
- * This value may be null.
- *
- * @return the control that currently is maximized or null
- */
-public Control getMaximizedControl(){
- //checkWidget();
- return this.maxControl;
-}
-/**
- * Answer the relative weight of each child in the SashForm. The weight represents the
- * percent of the total width (if SashForm has Horizontal orientation) or
- * total height (if SashForm has Vertical orientation) each control occupies.
- * The weights are returned in order of the creation of the widgets (weight[0]
- * corresponds to the weight of the first child created).
- *
- * @return the relative weight of each child
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-
-public int[] getWeights() {
- checkWidget();
- Control[] cArray = getControls(false);
- float[] ratios = new float[cArray.length];
- for (int i = 0; i < cArray.length; i++) {
- Float ratio = (Float)cArray[i].getData(LAYOUT_RATIO);
- if (ratio != null) {
- ratios[i] = ratio.floatValue();
- } else {
- ratios[i] = (float)0.2;
- }
- }
-
- int[] weights = new int[cArray.length];
- for (int i = 0; i < weights.length; i++) {
- weights[i] = (int)(ratios[i] * 1000);
- }
- return weights;
-}
-private Control[] getControls(boolean onlyVisible) {
- Control[] children = getChildren();
- Control[] controls = new Control[0];
- for (int i = 0; i < children.length; i++) {
- if (children[i] instanceof Sash) continue;
- if (onlyVisible && !children[i].getVisible()) continue;
-
- Control[] newControls = new Control[controls.length + 1];
- System.arraycopy(controls, 0, newControls, 0, controls.length);
- newControls[controls.length] = children[i];
- controls = newControls;
- }
- return controls;
-}
-public void layout(boolean changed) {
- checkWidget();
- Rectangle area = getClientArea();
- if (area.width == 0 || area.height == 0) return;
-
- Control[] newControls = getControls(true);
- if (controls.length == 0 && newControls.length == 0) return;
- controls = newControls;
-
- if (maxControl != null && !maxControl.isDisposed()) {
- for (int i= 0; i < controls.length; i++){
- if (controls[i] != maxControl) {
- controls[i].setBounds(-200, -200, 0, 0);
- } else {
- controls[i].setBounds(area);
- }
- }
- return;
- }
-
- // keep just the right number of sashes
- if (sashes.length < controls.length - 1) {
- Sash[] newSashes = new Sash[controls.length - 1];
- System.arraycopy(sashes, 0, newSashes, 0, sashes.length);
- int sashOrientation = (orientation == SWT.HORIZONTAL) ? SWT.VERTICAL : SWT.HORIZONTAL;
- for (int i = sashes.length; i < newSashes.length; i++) {
- newSashes[i] = new Sash(this, sashOrientation);
- newSashes[i].addListener(SWT.Selection, sashListener);
- }
- sashes = newSashes;
- }
- if (sashes.length > controls.length - 1) {
- if (controls.length == 0) {
- for (int i = 0; i < sashes.length; i++) {
- sashes[i].dispose();
- }
- sashes = new Sash[0];
- } else {
- Sash[] newSashes = new Sash[controls.length - 1];
- System.arraycopy(sashes, 0, newSashes, 0, newSashes.length);
- for (int i = controls.length - 1; i < sashes.length; i++) {
- sashes[i].dispose();
- }
- sashes = newSashes;
- }
- }
-
- if (controls.length == 0) return;
-
- // get the ratios
- float[] ratios = new float[controls.length];
- float total = 0;
- for (int i = 0; i < controls.length; i++) {
- Float ratio = (Float)controls[i].getData(LAYOUT_RATIO);
- if (ratio != null) {
- ratios[i] = ratio.floatValue();
- } else {
- ratios[i] = (float)0.2;
- }
- total += ratios[i];
- }
-
- if (orientation == SWT.HORIZONTAL) {
- total += (float)sashes.length * ((float)SASH_WIDTH / (float)area.width);
- } else {
- total += (float)sashes.length * ((float)SASH_WIDTH / (float)area.height);
- }
-
- if (orientation == SWT.HORIZONTAL) {
- int width = (int)((ratios[0] / total) * (float)area.width);
- int x = area.x;
- controls[0].setBounds(x, area.y, width, area.height);
- x += width;
- for (int i = 1; i < controls.length - 1; i++) {
- sashes[i - 1].setBounds(x, area.y, SASH_WIDTH, area.height);
- x += SASH_WIDTH;
- width = (int)((ratios[i] / total) * (float)area.width);
- controls[i].setBounds(x, area.y, width, area.height);
- x += width;
- }
- if (controls.length > 1) {
- sashes[sashes.length - 1].setBounds(x, area.y, SASH_WIDTH, area.height);
- x += SASH_WIDTH;
- width = area.width - x;
- controls[controls.length - 1].setBounds(x, area.y, width, area.height);
- }
- } else {
- int height = (int)((ratios[0] / total) * (float)area.height);
- int y = area.y;
- controls[0].setBounds(area.x, y, area.width, height);
- y += height;
- for (int i = 1; i < controls.length - 1; i++) {
- sashes[i - 1].setBounds(area.x, y, area.width, SASH_WIDTH);
- y += SASH_WIDTH;
- height = (int)((ratios[i] / total) * (float)area.height);
- controls[i].setBounds(area.x, y, area.width, height);
- y += height;
- }
- if (controls.length > 1) {
- sashes[sashes.length - 1].setBounds(area.x, y, area.width, SASH_WIDTH);
- y += SASH_WIDTH;
- height = area.height - y;
- controls[controls.length - 1].setBounds(area.x, y, area.width, height);
- }
-
- }
-}
-private void onDragSash(Event event) {
- if (event.detail == SWT.DRAG) {
- // constrain feedback
- Rectangle area = getClientArea();
- if (orientation == SWT.HORIZONTAL) {
- event.x = Math.min(Math.max(DRAG_MINIMUM, event.x), area.width - DRAG_MINIMUM);
- } else {
- event.y = Math.min(Math.max(DRAG_MINIMUM, event.y), area.height - DRAG_MINIMUM);
- }
- return;
- }
-
- Sash sash = (Sash)event.widget;
- int sashIndex = -1;
- for (int i= 0; i < sashes.length; i++) {
- if (sashes[i] == sash) {
- sashIndex = i;
- break;
- }
- }
- if (sashIndex == -1) return;
-
- Control c1 = controls[sashIndex];
- Control c2 = controls[sashIndex + 1];
- Rectangle b1 = c1.getBounds();
- Rectangle b2 = c2.getBounds();
-
- Rectangle sashBounds = sash.getBounds();
- Rectangle area = getClientArea();
- if (orientation == SWT.HORIZONTAL) {
- int shift = event.x - sashBounds.x;
- b1.width += shift;
- b2.x += shift;
- b2.width -= shift;
- if (b1.width < DRAG_MINIMUM || b2.width < DRAG_MINIMUM) {
- return;
- }
- c1.setData(LAYOUT_RATIO, new Float((float)b1.width / (float)area.width));
- c2.setData(LAYOUT_RATIO, new Float((float)b2.width / (float)area.width));
- } else {
- int shift = event.y - sashBounds.y;
- b1.height += shift;
- b2.y += shift;
- b2.height -= shift;
- if (b1.height < DRAG_MINIMUM || b2.height < DRAG_MINIMUM) {
- return;
- }
- c1.setData(LAYOUT_RATIO, new Float((float)b1.height / (float)area.height));
- c2.setData(LAYOUT_RATIO, new Float((float)b2.height / (float)area.height));
- }
-
- c1.setBounds(b1);
- sash.setBounds(event.x, event.y, event.width, event.height);
- c2.setBounds(b2);
-}
-/**
- * If orientation is SWT.HORIZONTAL, lay the controls in the SashForm
- * out side by side. If orientation is SWT.VERTICAL, lay the
- * controls in the SashForm out top to bottom.
- *
- * @param orientation SWT.HORIZONTAL or SWT.VERTICAL
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - if the value of orientation is not SWT.HORIZONTAL or SWT.VERTICAL
- * </ul>
- */
-public void setOrientation(int orientation) {
- checkWidget();
- if (this.orientation == orientation) return;
- if (orientation != SWT.HORIZONTAL && orientation != SWT.VERTICAL) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- this.orientation = orientation;
-
- int sashOrientation = (orientation == SWT.HORIZONTAL) ? SWT.VERTICAL : SWT.HORIZONTAL;
- for (int i = 0; i < sashes.length; i++) {
- sashes[i].dispose();
- sashes[i] = new Sash(this, sashOrientation);
- sashes[i].addListener(SWT.Selection, sashListener);
- }
- layout();
-}
-public void setLayout (Layout layout) {
- checkWidget();
-}
-/**
- * Specify the control that should take up the entire client area of the SashForm.
- * If one control has been maximized, and this method is called with a different control,
- * the previous control will be minimized and the new control will be maximized..
- * if the value of control is null, the SashForm will minimize all controls and return to
- * the default layout where all controls are laid out separated by sashes.
- *
- * @param control the control to be maximized or null
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setMaximizedControl(Control control){
- checkWidget();
- if (control == null) {
- if (maxControl != null) {
- this.maxControl = null;
- layout();
- for (int i= 0; i < sashes.length; i++){
- sashes[i].setVisible(true);
- }
- }
- return;
- }
-
- for (int i= 0; i < sashes.length; i++){
- sashes[i].setVisible(false);
- }
- maxControl = control;
- layout();
-}
-
-/**
- * Specify the relative weight of each child in the SashForm. This will determine
- * what percent of the total width (if SashForm has Horizontal orientation) or
- * total height (if SashForm has Vertical orientation) each control will occupy.
- * The weights must be positive values and there must be an entry for each
- * non-sash child of the SashForm.
- *
- * @param weights the relative weight of each child
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - if the weights value is null or of incorrect length (must match the number of children)</li>
- * </ul>
- */
-public void setWeights(int[] weights) {
- checkWidget();
- Control[] cArray = getControls(false);
- if (weights == null || weights.length != cArray.length) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
-
- int total = 0;
- for (int i = 0; i < weights.length; i++) {
- if (weights[i] < 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- total += weights[i];
- }
- if (total == 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- for (int i = 0; i < cArray.length; i++) {
- cArray[i].setData(LAYOUT_RATIO, new Float((float)weights[i] / (float)total));
- }
-
- layout();
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * The SashForm lays out its children in a Row or Column arrangement (as specified
+ * by the orientation) and places a Sash between the children.
+ * One child may be maximized to occupy the entire size of the SashForm.
+ * The relative sizes of the children may be specfied using weights.
+ *
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b><dd>HORIZONTAL, VERTICAL
+ * </dl>
+ */
+public class SashForm extends Composite {
+
+ public int SASH_WIDTH = 3;
+
+ private static final int DRAG_MINIMUM = 20;
+
+ private int orientation = SWT.HORIZONTAL;
+ private Sash[] sashes = new Sash[0];
+ private Control[] controls = new Control[0];
+ private Control maxControl = null;
+ private Listener sashListener;
+ private final static String LAYOUT_RATIO = "layout ratio"; //$NON-NLS-1$
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#HORIZONTAL
+ * @see SWT#VERTICAL
+ * @see #getStyle
+ */
+public SashForm(Composite parent, int style) {
+ super(parent, checkStyle(style));
+ if ((style & SWT.VERTICAL) != 0){
+ orientation = SWT.VERTICAL;
+ }
+
+ this.addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ layout(true);
+ }
+ });
+
+ sashListener = new Listener() {
+ public void handleEvent(Event e) {
+ onDragSash(e);
+ }
+ };
+}
+private static int checkStyle (int style) {
+ int mask = SWT.BORDER;
+ return style & mask;
+}
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget();
+ Control[] controls = getControls(true);
+ if (controls.length == 0) return new Point(wHint, hHint);
+
+ int width = 0;
+ int height = 0;
+ boolean vertical = (orientation == SWT.VERTICAL);
+ if (vertical) {
+ height += (controls.length - 1) * SASH_WIDTH;
+ } else {
+ width += (controls.length - 1) * SASH_WIDTH;
+ }
+ for (int i = 0; i < controls.length; i++) {
+ if (vertical) {
+ Point size = controls[i].computeSize(wHint, SWT.DEFAULT);
+ height += size.y;
+ width = Math.max(width, size.x);
+ } else {
+ Point size = controls[i].computeSize(SWT.DEFAULT, hHint);
+ width += size.x;
+ height = Math.max(height, size.y);
+ }
+ }
+ if (wHint != SWT.DEFAULT) width = wHint;
+ if (hHint != SWT.DEFAULT) height = hHint;
+
+ return new Point(width, height);
+}
+/**
+ * Returns SWT.HORIZONTAL if the controls in the SashForm are laid out side by side
+ * or SWT.VERTICAL if the controls in the SashForm are laid out top to bottom.
+ *
+ * @return SWT.HORIZONTAL or SWT.VERTICAL
+ */
+public int getOrientation() {
+ //checkWidget();
+ return orientation;
+}
+/**
+ * Answer the control that currently is maximized in the SashForm.
+ * This value may be null.
+ *
+ * @return the control that currently is maximized or null
+ */
+public Control getMaximizedControl(){
+ //checkWidget();
+ return this.maxControl;
+}
+/**
+ * Answer the relative weight of each child in the SashForm. The weight represents the
+ * percent of the total width (if SashForm has Horizontal orientation) or
+ * total height (if SashForm has Vertical orientation) each control occupies.
+ * The weights are returned in order of the creation of the widgets (weight[0]
+ * corresponds to the weight of the first child created).
+ *
+ * @return the relative weight of each child
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+
+public int[] getWeights() {
+ checkWidget();
+ Control[] cArray = getControls(false);
+ float[] ratios = new float[cArray.length];
+ for (int i = 0; i < cArray.length; i++) {
+ Float ratio = (Float)cArray[i].getData(LAYOUT_RATIO);
+ if (ratio != null) {
+ ratios[i] = ratio.floatValue();
+ } else {
+ ratios[i] = (float)0.2;
+ }
+ }
+
+ int[] weights = new int[cArray.length];
+ for (int i = 0; i < weights.length; i++) {
+ weights[i] = (int)(ratios[i] * 1000);
+ }
+ return weights;
+}
+private Control[] getControls(boolean onlyVisible) {
+ Control[] children = getChildren();
+ Control[] controls = new Control[0];
+ for (int i = 0; i < children.length; i++) {
+ if (children[i] instanceof Sash) continue;
+ if (onlyVisible && !children[i].getVisible()) continue;
+
+ Control[] newControls = new Control[controls.length + 1];
+ System.arraycopy(controls, 0, newControls, 0, controls.length);
+ newControls[controls.length] = children[i];
+ controls = newControls;
+ }
+ return controls;
+}
+public void layout(boolean changed) {
+ checkWidget();
+ Rectangle area = getClientArea();
+ if (area.width == 0 || area.height == 0) return;
+
+ Control[] newControls = getControls(true);
+ if (controls.length == 0 && newControls.length == 0) return;
+ controls = newControls;
+
+ if (maxControl != null && !maxControl.isDisposed()) {
+ for (int i= 0; i < controls.length; i++){
+ if (controls[i] != maxControl) {
+ controls[i].setBounds(-200, -200, 0, 0);
+ } else {
+ controls[i].setBounds(area);
+ }
+ }
+ return;
+ }
+
+ // keep just the right number of sashes
+ if (sashes.length < controls.length - 1) {
+ Sash[] newSashes = new Sash[controls.length - 1];
+ System.arraycopy(sashes, 0, newSashes, 0, sashes.length);
+ int sashOrientation = (orientation == SWT.HORIZONTAL) ? SWT.VERTICAL : SWT.HORIZONTAL;
+ for (int i = sashes.length; i < newSashes.length; i++) {
+ newSashes[i] = new Sash(this, sashOrientation);
+ newSashes[i].addListener(SWT.Selection, sashListener);
+ }
+ sashes = newSashes;
+ }
+ if (sashes.length > controls.length - 1) {
+ if (controls.length == 0) {
+ for (int i = 0; i < sashes.length; i++) {
+ sashes[i].dispose();
+ }
+ sashes = new Sash[0];
+ } else {
+ Sash[] newSashes = new Sash[controls.length - 1];
+ System.arraycopy(sashes, 0, newSashes, 0, newSashes.length);
+ for (int i = controls.length - 1; i < sashes.length; i++) {
+ sashes[i].dispose();
+ }
+ sashes = newSashes;
+ }
+ }
+
+ if (controls.length == 0) return;
+
+ // get the ratios
+ float[] ratios = new float[controls.length];
+ float total = 0;
+ for (int i = 0; i < controls.length; i++) {
+ Float ratio = (Float)controls[i].getData(LAYOUT_RATIO);
+ if (ratio != null) {
+ ratios[i] = ratio.floatValue();
+ } else {
+ ratios[i] = (float)0.2;
+ }
+ total += ratios[i];
+ }
+
+ if (orientation == SWT.HORIZONTAL) {
+ total += (float)sashes.length * ((float)SASH_WIDTH / (float)area.width);
+ } else {
+ total += (float)sashes.length * ((float)SASH_WIDTH / (float)area.height);
+ }
+
+ if (orientation == SWT.HORIZONTAL) {
+ int width = (int)((ratios[0] / total) * (float)area.width);
+ int x = area.x;
+ controls[0].setBounds(x, area.y, width, area.height);
+ x += width;
+ for (int i = 1; i < controls.length - 1; i++) {
+ sashes[i - 1].setBounds(x, area.y, SASH_WIDTH, area.height);
+ x += SASH_WIDTH;
+ width = (int)((ratios[i] / total) * (float)area.width);
+ controls[i].setBounds(x, area.y, width, area.height);
+ x += width;
+ }
+ if (controls.length > 1) {
+ sashes[sashes.length - 1].setBounds(x, area.y, SASH_WIDTH, area.height);
+ x += SASH_WIDTH;
+ width = area.width - x;
+ controls[controls.length - 1].setBounds(x, area.y, width, area.height);
+ }
+ } else {
+ int height = (int)((ratios[0] / total) * (float)area.height);
+ int y = area.y;
+ controls[0].setBounds(area.x, y, area.width, height);
+ y += height;
+ for (int i = 1; i < controls.length - 1; i++) {
+ sashes[i - 1].setBounds(area.x, y, area.width, SASH_WIDTH);
+ y += SASH_WIDTH;
+ height = (int)((ratios[i] / total) * (float)area.height);
+ controls[i].setBounds(area.x, y, area.width, height);
+ y += height;
+ }
+ if (controls.length > 1) {
+ sashes[sashes.length - 1].setBounds(area.x, y, area.width, SASH_WIDTH);
+ y += SASH_WIDTH;
+ height = area.height - y;
+ controls[controls.length - 1].setBounds(area.x, y, area.width, height);
+ }
+
+ }
+}
+private void onDragSash(Event event) {
+ if (event.detail == SWT.DRAG) {
+ // constrain feedback
+ Rectangle area = getClientArea();
+ if (orientation == SWT.HORIZONTAL) {
+ event.x = Math.min(Math.max(DRAG_MINIMUM, event.x), area.width - DRAG_MINIMUM);
+ } else {
+ event.y = Math.min(Math.max(DRAG_MINIMUM, event.y), area.height - DRAG_MINIMUM);
+ }
+ return;
+ }
+
+ Sash sash = (Sash)event.widget;
+ int sashIndex = -1;
+ for (int i= 0; i < sashes.length; i++) {
+ if (sashes[i] == sash) {
+ sashIndex = i;
+ break;
+ }
+ }
+ if (sashIndex == -1) return;
+
+ Control c1 = controls[sashIndex];
+ Control c2 = controls[sashIndex + 1];
+ Rectangle b1 = c1.getBounds();
+ Rectangle b2 = c2.getBounds();
+
+ Rectangle sashBounds = sash.getBounds();
+ Rectangle area = getClientArea();
+ if (orientation == SWT.HORIZONTAL) {
+ int shift = event.x - sashBounds.x;
+ b1.width += shift;
+ b2.x += shift;
+ b2.width -= shift;
+ if (b1.width < DRAG_MINIMUM || b2.width < DRAG_MINIMUM) {
+ return;
+ }
+ c1.setData(LAYOUT_RATIO, new Float((float)b1.width / (float)area.width));
+ c2.setData(LAYOUT_RATIO, new Float((float)b2.width / (float)area.width));
+ } else {
+ int shift = event.y - sashBounds.y;
+ b1.height += shift;
+ b2.y += shift;
+ b2.height -= shift;
+ if (b1.height < DRAG_MINIMUM || b2.height < DRAG_MINIMUM) {
+ return;
+ }
+ c1.setData(LAYOUT_RATIO, new Float((float)b1.height / (float)area.height));
+ c2.setData(LAYOUT_RATIO, new Float((float)b2.height / (float)area.height));
+ }
+
+ c1.setBounds(b1);
+ sash.setBounds(event.x, event.y, event.width, event.height);
+ c2.setBounds(b2);
+}
+/**
+ * If orientation is SWT.HORIZONTAL, lay the controls in the SashForm
+ * out side by side. If orientation is SWT.VERTICAL, lay the
+ * controls in the SashForm out top to bottom.
+ *
+ * @param orientation SWT.HORIZONTAL or SWT.VERTICAL
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the value of orientation is not SWT.HORIZONTAL or SWT.VERTICAL
+ * </ul>
+ */
+public void setOrientation(int orientation) {
+ checkWidget();
+ if (this.orientation == orientation) return;
+ if (orientation != SWT.HORIZONTAL && orientation != SWT.VERTICAL) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ this.orientation = orientation;
+
+ int sashOrientation = (orientation == SWT.HORIZONTAL) ? SWT.VERTICAL : SWT.HORIZONTAL;
+ for (int i = 0; i < sashes.length; i++) {
+ sashes[i].dispose();
+ sashes[i] = new Sash(this, sashOrientation);
+ sashes[i].addListener(SWT.Selection, sashListener);
+ }
+ layout();
+}
+public void setLayout (Layout layout) {
+ checkWidget();
+}
+/**
+ * Specify the control that should take up the entire client area of the SashForm.
+ * If one control has been maximized, and this method is called with a different control,
+ * the previous control will be minimized and the new control will be maximized..
+ * if the value of control is null, the SashForm will minimize all controls and return to
+ * the default layout where all controls are laid out separated by sashes.
+ *
+ * @param control the control to be maximized or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMaximizedControl(Control control){
+ checkWidget();
+ if (control == null) {
+ if (maxControl != null) {
+ this.maxControl = null;
+ layout();
+ for (int i= 0; i < sashes.length; i++){
+ sashes[i].setVisible(true);
+ }
+ }
+ return;
+ }
+
+ for (int i= 0; i < sashes.length; i++){
+ sashes[i].setVisible(false);
+ }
+ maxControl = control;
+ layout();
+}
+
+/**
+ * Specify the relative weight of each child in the SashForm. This will determine
+ * what percent of the total width (if SashForm has Horizontal orientation) or
+ * total height (if SashForm has Vertical orientation) each control will occupy.
+ * The weights must be positive values and there must be an entry for each
+ * non-sash child of the SashForm.
+ *
+ * @param weights the relative weight of each child
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the weights value is null or of incorrect length (must match the number of children)</li>
+ * </ul>
+ */
+public void setWeights(int[] weights) {
+ checkWidget();
+ Control[] cArray = getControls(false);
+ if (weights == null || weights.length != cArray.length) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+
+ int total = 0;
+ for (int i = 0; i < weights.length; i++) {
+ if (weights[i] < 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ total += weights[i];
+ }
+ if (total == 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ for (int i = 0; i < cArray.length; i++) {
+ cArray[i].setData(LAYOUT_RATIO, new Float((float)weights[i] / (float)total));
+ }
+
+ layout();
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ScrolledComposite.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ScrolledComposite.java
index f8c53bde76..09476a36ef 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ScrolledComposite.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ScrolledComposite.java
@@ -1,583 +1,583 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * A ScrolledComposite provides scrollbars and will scroll its content when the user
- * uses the scrollbars.
- *
- *
- * <p>There are two ways to use the ScrolledComposite:
- *
- * <p>
- * 1) Set the size of the control that is being scrolled and the ScrolledComposite
- * will show scrollbars when the contained control can not be fully seen.
- *
- * 2) The second way imitates the way a browser would work. Set the minimum size of
- * the control and the ScrolledComposite will show scroll bars if the visible area is
- * less than the minimum size of the control and it will expand the size of the control
- * if the visible area is greater than the minimum size. This requires invoking
- * both setMinWidth(), setMinHeight() and setExpandHorizontal(), setExpandVertical().
- *
- * <code><pre>
- * public static void main (String [] args) {
- * Display display = new Display ();
- * Color red = display.getSystemColor(SWT.COLOR_RED);
- * Color blue = display.getSystemColor(SWT.COLOR_BLUE);
- * Shell shell = new Shell (display);
- * shell.setLayout(new FillLayout());
- *
- * // set the size of the scrolled content - method 1
- * final ScrolledComposite sc1 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
- * final Composite c1 = new Composite(sc1, SWT.NONE);
- * sc1.setContent(c1);
- * c1.setBackground(red);
- * GridLayout layout = new GridLayout();
- * layout.numColumns = 4;
- * c1.setLayout(layout);
- * Button b1 = new Button (c1, SWT.PUSH);
- * b1.setText("first button");
- * c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
- *
- * // set the minimum width and height of the scrolled content - method 2
- * final ScrolledComposite sc2 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
- * sc2.setExpandHorizontal(true);
- * sc2.setExpandVertical(true);
- * final Composite c2 = new Composite(sc2, SWT.NONE);
- * sc2.setContent(c2);
- * c2.setBackground(blue);
- * layout = new GridLayout();
- * layout.numColumns = 4;
- * c2.setLayout(layout);
- * Button b2 = new Button (c2, SWT.PUSH);
- * b2.setText("first button");
- * sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
- *
- * Button add = new Button (shell, SWT.PUSH);
- * add.setText("add children");
- * final int[] index = new int[]{0};
- * add.addListener(SWT.Selection, new Listener() {
- * public void handleEvent(Event e) {
- * index[0]++;
- * Button button = new Button(c1, SWT.PUSH);
- * button.setText("button "+index[0]);
- * // reset size of content so children can be seen - method 1
- * c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
- * c1.layout();
- *
- * button = new Button(c2, SWT.PUSH);
- * button.setText("button "+index[0]);
- * // reset the minimum width and height so children can be seen - method 2
- * sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
- * c2.layout();
- * }
- * });
- *
- * shell.open ();
- * while (!shell.isDisposed ()) {
- * if (!display.readAndDispatch ()) display.sleep ();
- * }
- * display.dispose ();
- * }
- * </pre></code>
- *
- * <dl>
- * <dt><b>Styles:</b><dd>H_SCROLL, V_SCROLL
- * </dl>
- */
-public class ScrolledComposite extends Composite {
-
- private Control content;
- private Listener contentListener;
-
- private int minHeight = 0;
- private int minWidth = 0;
- private boolean expandHorizontal = false;
- private boolean expandVertical = false;
- private boolean alwaysShowScroll = false;
- private boolean inResize = false;
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#H_SCROLL
- * @see SWT#V_SCROLL
- * @see #getStyle
- */
-public ScrolledComposite(Composite parent, int style) {
- super(parent, checkStyle(style));
-
- ScrollBar hBar = getHorizontalBar ();
- if (hBar != null) {
- hBar.addListener (SWT.Selection, new Listener () {
- public void handleEvent (Event e) {
- hScroll();
- }
- });
- }
-
- ScrollBar vBar = getVerticalBar ();
- if (vBar != null) {
- vBar.addListener (SWT.Selection, new Listener () {
- public void handleEvent (Event e) {
- vScroll();
- }
- });
- }
-
- addListener (SWT.Resize, new Listener () {
- public void handleEvent (Event e) {
- resize();
- }
- });
-
- contentListener = new Listener() {
- public void handleEvent(Event e) {
- if (e.type != SWT.Resize) return;
- resize();
- }
- };
-}
-
-private static int checkStyle (int style) {
- int mask = SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
- return style & mask;
-}
-
-public Point computeSize (int wHint, int hHint, boolean changed) {
- checkWidget ();
- /*
- * When a composite does layout without using a layout
- * manager, it must take into account the preferred size
- * of it's children when computing it's preferred size in
- * the same way that a layout manager would. In particular,
- * when a scrolled composite hides the scroll bars and
- * places a child to fill the client area, then repeated
- * calls to compute the preferred size of the scrolled
- * composite should not keep adding in the space used by
- * the scroll bars.
- */
- if (content == null) {
- return super.computeSize (wHint, hHint, changed);
- }
- Point size = content.computeSize (wHint, hHint, changed);
- Rectangle trim = computeTrim (0, 0, size.x, size.y);
- return new Point (trim.width, trim.height);
-}
-
-/**
- * Returns the Always Show Scrollbars flag. True if the scrollbars are
- * always shown even if they are not required. False if the scrollbars are only
- * visible when some part of the composite needs to be scrolled to be seen.
- * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
- * horizontal and vertical directions.
- *
- * @return the Always Show Scrollbars flag value
- */
-public boolean getAlwaysShowScrollBars() {
- //checkWidget();
- return alwaysShowScroll;
-}
-
-/**
- * Get the content that is being scrolled.
- *
- * @return the control displayed in the content area
- */
-public Control getContent() {
- //checkWidget();
- return content;
-}
-
-private void hScroll() {
- if (content == null) return;
- Point location = content.getLocation ();
- ScrollBar hBar = getHorizontalBar ();
- int hSelection = hBar.getSelection ();
- content.setLocation (-hSelection, location.y);
-}
-
-public void layout(boolean changed) {
- checkWidget();
- if (content == null) return;
- Rectangle contentRect = content.getBounds();
- ScrollBar hBar = getHorizontalBar ();
- ScrollBar vBar = getVerticalBar ();
- if (!alwaysShowScroll) {
- boolean hVisible = needHScroll(contentRect, false);
- boolean vVisible = needVScroll(contentRect, hVisible);
- if (!hVisible && vVisible) hVisible = needHScroll(contentRect, vVisible);
- if (hBar != null) hBar.setVisible(hVisible);
- if (vBar != null) vBar.setVisible(vVisible);
- }
-
- Rectangle hostRect = getClientArea();
- if (expandHorizontal) {
- contentRect.width = Math.max(minWidth, hostRect.width);
- }
- if (expandVertical) {
- contentRect.height = Math.max(minHeight, hostRect.height);
- }
-
- if (hBar != null) {
- hBar.setMaximum (contentRect.width);
- hBar.setThumb (Math.min (contentRect.width, hostRect.width));
- int hPage = contentRect.width - hostRect.width;
- int hSelection = hBar.getSelection ();
- if (hSelection >= hPage) {
- if (hPage <= 0) {
- hSelection = 0;
- hBar.setSelection(0);
- }
- contentRect.x = -hSelection;
- }
- }
-
- if (vBar != null) {
- vBar.setMaximum (contentRect.height);
- vBar.setThumb (Math.min (contentRect.height, hostRect.height));
- int vPage = contentRect.height - hostRect.height;
- int vSelection = vBar.getSelection ();
- if (vSelection >= vPage) {
- if (vPage <= 0) {
- vSelection = 0;
- vBar.setSelection(0);
- }
- contentRect.y = -vSelection;
- }
- }
-
- content.setBounds (contentRect);
-}
-
-private boolean needHScroll(Rectangle contentRect, boolean vVisible) {
- ScrollBar hBar = getHorizontalBar();
- if (hBar == null) return false;
-
- Rectangle hostRect = getBounds();
- int border = getBorderWidth();
- hostRect.width -= 2*border;
- ScrollBar vBar = getVerticalBar();
- if (vVisible && vBar != null) hostRect.width -= vBar.getSize().x;
-
- if (!expandHorizontal && contentRect.width > hostRect.width) return true;
- if (expandHorizontal && minWidth > hostRect.width) return true;
- return false;
-}
-
-private boolean needVScroll(Rectangle contentRect, boolean hVisible) {
- ScrollBar vBar = getVerticalBar();
- if (vBar == null) return false;
-
- Rectangle hostRect = getBounds();
- int border = getBorderWidth();
- hostRect.height -= 2*border;
- ScrollBar hBar = getHorizontalBar();
- if (hVisible && hBar != null) hostRect.height -= hBar.getSize().y;
-
- if (!expandHorizontal && contentRect.height > hostRect.height) return true;
- if (expandHorizontal && minHeight > hostRect.height) return true;
- return false;
-}
-
-private void resize() {
- if (inResize) return;
- inResize = true;
- layout();
- inResize = false;
-}
-/**
- * Return the point in the content that currenly appears in the top left
- * corner of the scrolled composite.
- *
- * @return the point in the content that currenly appears in the top left
- * corner of the scrolled composite. If no content has been set, this returns
- * (0, 0).
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.0
- */
-public Point getOrigin() {
- checkWidget();
- if (content == null) return new Point(0, 0);
- Point location = content.getLocation();
- return new Point(-location.x, -location.y);
-}
-/**
- * Scrolls the content so that the specified point in the content is in the top
- * left corner. If no content has been set, nothing will occur.
- *
- * Negative values will be ignored. Values greater than the maximum scroll
- * distance will result in scrolling to the end of the scrollbar.
- *
- * @param origin the point on the content to appear in the top left corner
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * <li>ERROR_INVALID_ARGUMENT - value of origin is outside of content
- * </ul>
- * @since 2.0
- */
-public void setOrigin(Point origin) {
- setOrigin(origin.x, origin.y);
-}
-/**
- * Scrolls the content so that the specified point in the content is in the top
- * left corner. If no content has been set, nothing will occur.
- *
- * Negative values will be ignored. Values greater than the maximum scroll
- * distance will result in scrolling to the end of the scrollbar.
- *
- * @param x the x coordinate of the content to appear in the top left corner
- *
- * @param y the y coordinate of the content to appear in the top left corner
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.0
- */
-public void setOrigin(int x, int y) {
- checkWidget();
- if (content == null) return;
- ScrollBar hBar = getHorizontalBar ();
- if (hBar != null) {
- hBar.setSelection(x);
- x = -hBar.getSelection ();
- } else {
- x = 0;
- }
- ScrollBar vBar = getVerticalBar ();
- if (vBar != null) {
- vBar.setSelection(y);
- y = -vBar.getSelection ();
- } else {
- y = 0;
- }
- content.setLocation(x, y);
-}
-/**
- * Set the Always Show Scrollbars flag. True if the scrollbars are
- * always shown even if they are not required. False if the scrollbars are only
- * visible when some part of the composite needs to be scrolled to be seen.
- * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
- * horizontal and vertical directions.
- *
- * @param show true to show the scrollbars even when not required, false to show scrollbars only when required
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setAlwaysShowScrollBars(boolean show) {
- checkWidget();
- if (show == alwaysShowScroll) return;
- alwaysShowScroll = show;
- ScrollBar hBar = getHorizontalBar ();
- if (hBar != null && alwaysShowScroll) hBar.setVisible(true);
- ScrollBar vBar = getVerticalBar ();
- if (vBar != null && alwaysShowScroll) vBar.setVisible(true);
- layout();
-}
-
-/**
- * Set the content that will be scrolled.
- *
- * @param content the control to be displayed in the content area
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setContent(Control content) {
- checkWidget();
- if (this.content != null && !this.content.isDisposed()) {
- this.content.removeListener(SWT.Resize, contentListener);
- this.content.setBounds(new Rectangle(-200, -200, 0, 0));
- }
-
- this.content = content;
- ScrollBar vBar = getVerticalBar ();
- ScrollBar hBar = getHorizontalBar ();
- if (this.content != null) {
- if (vBar != null) {
- vBar.setMaximum (0);
- vBar.setThumb (0);
- vBar.setSelection(0);
- }
- if (hBar != null) {
- hBar.setMaximum (0);
- hBar.setThumb (0);
- hBar.setSelection(0);
- }
- content.setLocation(0, 0);
- layout();
- this.content.addListener(SWT.Resize, contentListener);
- } else {
- if (hBar != null) hBar.setVisible(alwaysShowScroll);
- if (vBar != null) vBar.setVisible(alwaysShowScroll);
- }
-}
-/**
- * Configure the ScrolledComposite to resize the content object to be as wide as the
- * ScrolledComposite when the width of the ScrolledComposite is greater than the
- * minimum width specified in setMinWidth. If the ScrolledComposite is less than the
- * minimum width, the content will not resized and instead the horizontal scroll bar will be
- * used to view the entire width.
- * If expand is false, this behaviour is turned off. By default, this behaviour is turned off.
- *
- * @param expand true to expand the content control to fill available horizontal space
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setExpandHorizontal(boolean expand) {
- checkWidget();
- if (expand == expandHorizontal) return;
- expandHorizontal = expand;
- layout();
-}
-/**
- * Configure the ScrolledComposite to resize the content object to be as tall as the
- * ScrolledComposite when the height of the ScrolledComposite is greater than the
- * minimum height specified in setMinHeight. If the ScrolledComposite is less than the
- * minimum height, the content will not resized and instead the vertical scroll bar will be
- * used to view the entire height.
- * If expand is false, this behaviour is turned off. By default, this behaviour is turned off.
- *
- * @param expand true to expand the content control to fill available vertical space
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setExpandVertical(boolean expand) {
- checkWidget();
- if (expand == expandVertical) return;
- expandVertical = expand;
- layout();
-}
-public void setLayout (Layout layout) {
- // do not allow a layout to be set on this class because layout is being handled by the resize listener
- checkWidget();
- return;
-}
-/**
- * Specify the minimum height at which the ScrolledComposite will begin scrolling the
- * content with the vertical scroll bar. This value is only relevant if
- * setExpandVertical(true) has been set.
- *
- * @param height the minimum height or 0 for default height
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setMinHeight(int height) {
- setMinSize(minWidth, height);
-}
-/**
- * Specify the minimum width and height at which the ScrolledComposite will begin scrolling the
- * content with the horizontal scroll bar. This value is only relevant if
- * setExpandHorizontal(true) and setExpandVertical(true) have been set.
- *
- * @param size the minimum size or null for the default size
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setMinSize(Point size) {
- if (size == null) {
- setMinSize(0, 0);
- } else {
- setMinSize(size.x, size.y);
- }
-}
-/**
- * Specify the minimum width and height at which the ScrolledComposite will begin scrolling the
- * content with the horizontal scroll bar. This value is only relevant if
- * setExpandHorizontal(true) and setExpandVertical(true) have been set.
- *
- * @param width the minimum width or 0 for default width
- * @param height the minimum height or 0 for default height
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setMinSize(int width, int height) {
- checkWidget();
- if (width == minWidth && height == minHeight) return;
- minWidth = Math.max(0, width);
- minHeight = Math.max(0, height);
- layout();
-}
-/**
- * Specify the minimum width at which the ScrolledComposite will begin scrolling the
- * content with the horizontal scroll bar. This value is only relevant if
- * setExpandHorizontal(true) has been set.
- *
- * @param width the minimum width or 0 for default width
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setMinWidth(int width) {
- setMinSize(width, minHeight);
-}
-
-private void vScroll() {
- if (content == null) return;
- Point location = content.getLocation ();
- ScrollBar vBar = getVerticalBar ();
- int vSelection = vBar.getSelection ();
- content.setLocation (location.x, -vSelection);
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A ScrolledComposite provides scrollbars and will scroll its content when the user
+ * uses the scrollbars.
+ *
+ *
+ * <p>There are two ways to use the ScrolledComposite:
+ *
+ * <p>
+ * 1) Set the size of the control that is being scrolled and the ScrolledComposite
+ * will show scrollbars when the contained control can not be fully seen.
+ *
+ * 2) The second way imitates the way a browser would work. Set the minimum size of
+ * the control and the ScrolledComposite will show scroll bars if the visible area is
+ * less than the minimum size of the control and it will expand the size of the control
+ * if the visible area is greater than the minimum size. This requires invoking
+ * both setMinWidth(), setMinHeight() and setExpandHorizontal(), setExpandVertical().
+ *
+ * <code><pre>
+ * public static void main (String [] args) {
+ * Display display = new Display ();
+ * Color red = display.getSystemColor(SWT.COLOR_RED);
+ * Color blue = display.getSystemColor(SWT.COLOR_BLUE);
+ * Shell shell = new Shell (display);
+ * shell.setLayout(new FillLayout());
+ *
+ * // set the size of the scrolled content - method 1
+ * final ScrolledComposite sc1 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+ * final Composite c1 = new Composite(sc1, SWT.NONE);
+ * sc1.setContent(c1);
+ * c1.setBackground(red);
+ * GridLayout layout = new GridLayout();
+ * layout.numColumns = 4;
+ * c1.setLayout(layout);
+ * Button b1 = new Button (c1, SWT.PUSH);
+ * b1.setText("first button");
+ * c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ *
+ * // set the minimum width and height of the scrolled content - method 2
+ * final ScrolledComposite sc2 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+ * sc2.setExpandHorizontal(true);
+ * sc2.setExpandVertical(true);
+ * final Composite c2 = new Composite(sc2, SWT.NONE);
+ * sc2.setContent(c2);
+ * c2.setBackground(blue);
+ * layout = new GridLayout();
+ * layout.numColumns = 4;
+ * c2.setLayout(layout);
+ * Button b2 = new Button (c2, SWT.PUSH);
+ * b2.setText("first button");
+ * sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ *
+ * Button add = new Button (shell, SWT.PUSH);
+ * add.setText("add children");
+ * final int[] index = new int[]{0};
+ * add.addListener(SWT.Selection, new Listener() {
+ * public void handleEvent(Event e) {
+ * index[0]++;
+ * Button button = new Button(c1, SWT.PUSH);
+ * button.setText("button "+index[0]);
+ * // reset size of content so children can be seen - method 1
+ * c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ * c1.layout();
+ *
+ * button = new Button(c2, SWT.PUSH);
+ * button.setText("button "+index[0]);
+ * // reset the minimum width and height so children can be seen - method 2
+ * sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ * c2.layout();
+ * }
+ * });
+ *
+ * shell.open ();
+ * while (!shell.isDisposed ()) {
+ * if (!display.readAndDispatch ()) display.sleep ();
+ * }
+ * display.dispose ();
+ * }
+ * </pre></code>
+ *
+ * <dl>
+ * <dt><b>Styles:</b><dd>H_SCROLL, V_SCROLL
+ * </dl>
+ */
+public class ScrolledComposite extends Composite {
+
+ private Control content;
+ private Listener contentListener;
+
+ private int minHeight = 0;
+ private int minWidth = 0;
+ private boolean expandHorizontal = false;
+ private boolean expandVertical = false;
+ private boolean alwaysShowScroll = false;
+ private boolean inResize = false;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#H_SCROLL
+ * @see SWT#V_SCROLL
+ * @see #getStyle
+ */
+public ScrolledComposite(Composite parent, int style) {
+ super(parent, checkStyle(style));
+
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.addListener (SWT.Selection, new Listener () {
+ public void handleEvent (Event e) {
+ hScroll();
+ }
+ });
+ }
+
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ vBar.addListener (SWT.Selection, new Listener () {
+ public void handleEvent (Event e) {
+ vScroll();
+ }
+ });
+ }
+
+ addListener (SWT.Resize, new Listener () {
+ public void handleEvent (Event e) {
+ resize();
+ }
+ });
+
+ contentListener = new Listener() {
+ public void handleEvent(Event e) {
+ if (e.type != SWT.Resize) return;
+ resize();
+ }
+ };
+}
+
+private static int checkStyle (int style) {
+ int mask = SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
+ return style & mask;
+}
+
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget ();
+ /*
+ * When a composite does layout without using a layout
+ * manager, it must take into account the preferred size
+ * of it's children when computing it's preferred size in
+ * the same way that a layout manager would. In particular,
+ * when a scrolled composite hides the scroll bars and
+ * places a child to fill the client area, then repeated
+ * calls to compute the preferred size of the scrolled
+ * composite should not keep adding in the space used by
+ * the scroll bars.
+ */
+ if (content == null) {
+ return super.computeSize (wHint, hHint, changed);
+ }
+ Point size = content.computeSize (wHint, hHint, changed);
+ Rectangle trim = computeTrim (0, 0, size.x, size.y);
+ return new Point (trim.width, trim.height);
+}
+
+/**
+ * Returns the Always Show Scrollbars flag. True if the scrollbars are
+ * always shown even if they are not required. False if the scrollbars are only
+ * visible when some part of the composite needs to be scrolled to be seen.
+ * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
+ * horizontal and vertical directions.
+ *
+ * @return the Always Show Scrollbars flag value
+ */
+public boolean getAlwaysShowScrollBars() {
+ //checkWidget();
+ return alwaysShowScroll;
+}
+
+/**
+ * Get the content that is being scrolled.
+ *
+ * @return the control displayed in the content area
+ */
+public Control getContent() {
+ //checkWidget();
+ return content;
+}
+
+private void hScroll() {
+ if (content == null) return;
+ Point location = content.getLocation ();
+ ScrollBar hBar = getHorizontalBar ();
+ int hSelection = hBar.getSelection ();
+ content.setLocation (-hSelection, location.y);
+}
+
+public void layout(boolean changed) {
+ checkWidget();
+ if (content == null) return;
+ Rectangle contentRect = content.getBounds();
+ ScrollBar hBar = getHorizontalBar ();
+ ScrollBar vBar = getVerticalBar ();
+ if (!alwaysShowScroll) {
+ boolean hVisible = needHScroll(contentRect, false);
+ boolean vVisible = needVScroll(contentRect, hVisible);
+ if (!hVisible && vVisible) hVisible = needHScroll(contentRect, vVisible);
+ if (hBar != null) hBar.setVisible(hVisible);
+ if (vBar != null) vBar.setVisible(vVisible);
+ }
+
+ Rectangle hostRect = getClientArea();
+ if (expandHorizontal) {
+ contentRect.width = Math.max(minWidth, hostRect.width);
+ }
+ if (expandVertical) {
+ contentRect.height = Math.max(minHeight, hostRect.height);
+ }
+
+ if (hBar != null) {
+ hBar.setMaximum (contentRect.width);
+ hBar.setThumb (Math.min (contentRect.width, hostRect.width));
+ int hPage = contentRect.width - hostRect.width;
+ int hSelection = hBar.getSelection ();
+ if (hSelection >= hPage) {
+ if (hPage <= 0) {
+ hSelection = 0;
+ hBar.setSelection(0);
+ }
+ contentRect.x = -hSelection;
+ }
+ }
+
+ if (vBar != null) {
+ vBar.setMaximum (contentRect.height);
+ vBar.setThumb (Math.min (contentRect.height, hostRect.height));
+ int vPage = contentRect.height - hostRect.height;
+ int vSelection = vBar.getSelection ();
+ if (vSelection >= vPage) {
+ if (vPage <= 0) {
+ vSelection = 0;
+ vBar.setSelection(0);
+ }
+ contentRect.y = -vSelection;
+ }
+ }
+
+ content.setBounds (contentRect);
+}
+
+private boolean needHScroll(Rectangle contentRect, boolean vVisible) {
+ ScrollBar hBar = getHorizontalBar();
+ if (hBar == null) return false;
+
+ Rectangle hostRect = getBounds();
+ int border = getBorderWidth();
+ hostRect.width -= 2*border;
+ ScrollBar vBar = getVerticalBar();
+ if (vVisible && vBar != null) hostRect.width -= vBar.getSize().x;
+
+ if (!expandHorizontal && contentRect.width > hostRect.width) return true;
+ if (expandHorizontal && minWidth > hostRect.width) return true;
+ return false;
+}
+
+private boolean needVScroll(Rectangle contentRect, boolean hVisible) {
+ ScrollBar vBar = getVerticalBar();
+ if (vBar == null) return false;
+
+ Rectangle hostRect = getBounds();
+ int border = getBorderWidth();
+ hostRect.height -= 2*border;
+ ScrollBar hBar = getHorizontalBar();
+ if (hVisible && hBar != null) hostRect.height -= hBar.getSize().y;
+
+ if (!expandHorizontal && contentRect.height > hostRect.height) return true;
+ if (expandHorizontal && minHeight > hostRect.height) return true;
+ return false;
+}
+
+private void resize() {
+ if (inResize) return;
+ inResize = true;
+ layout();
+ inResize = false;
+}
+/**
+ * Return the point in the content that currenly appears in the top left
+ * corner of the scrolled composite.
+ *
+ * @return the point in the content that currenly appears in the top left
+ * corner of the scrolled composite. If no content has been set, this returns
+ * (0, 0).
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public Point getOrigin() {
+ checkWidget();
+ if (content == null) return new Point(0, 0);
+ Point location = content.getLocation();
+ return new Point(-location.x, -location.y);
+}
+/**
+ * Scrolls the content so that the specified point in the content is in the top
+ * left corner. If no content has been set, nothing will occur.
+ *
+ * Negative values will be ignored. Values greater than the maximum scroll
+ * distance will result in scrolling to the end of the scrollbar.
+ *
+ * @param origin the point on the content to appear in the top left corner
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * <li>ERROR_INVALID_ARGUMENT - value of origin is outside of content
+ * </ul>
+ * @since 2.0
+ */
+public void setOrigin(Point origin) {
+ setOrigin(origin.x, origin.y);
+}
+/**
+ * Scrolls the content so that the specified point in the content is in the top
+ * left corner. If no content has been set, nothing will occur.
+ *
+ * Negative values will be ignored. Values greater than the maximum scroll
+ * distance will result in scrolling to the end of the scrollbar.
+ *
+ * @param x the x coordinate of the content to appear in the top left corner
+ *
+ * @param y the y coordinate of the content to appear in the top left corner
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setOrigin(int x, int y) {
+ checkWidget();
+ if (content == null) return;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setSelection(x);
+ x = -hBar.getSelection ();
+ } else {
+ x = 0;
+ }
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ vBar.setSelection(y);
+ y = -vBar.getSelection ();
+ } else {
+ y = 0;
+ }
+ content.setLocation(x, y);
+}
+/**
+ * Set the Always Show Scrollbars flag. True if the scrollbars are
+ * always shown even if they are not required. False if the scrollbars are only
+ * visible when some part of the composite needs to be scrolled to be seen.
+ * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
+ * horizontal and vertical directions.
+ *
+ * @param show true to show the scrollbars even when not required, false to show scrollbars only when required
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlwaysShowScrollBars(boolean show) {
+ checkWidget();
+ if (show == alwaysShowScroll) return;
+ alwaysShowScroll = show;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null && alwaysShowScroll) hBar.setVisible(true);
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null && alwaysShowScroll) vBar.setVisible(true);
+ layout();
+}
+
+/**
+ * Set the content that will be scrolled.
+ *
+ * @param content the control to be displayed in the content area
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setContent(Control content) {
+ checkWidget();
+ if (this.content != null && !this.content.isDisposed()) {
+ this.content.removeListener(SWT.Resize, contentListener);
+ this.content.setBounds(new Rectangle(-200, -200, 0, 0));
+ }
+
+ this.content = content;
+ ScrollBar vBar = getVerticalBar ();
+ ScrollBar hBar = getHorizontalBar ();
+ if (this.content != null) {
+ if (vBar != null) {
+ vBar.setMaximum (0);
+ vBar.setThumb (0);
+ vBar.setSelection(0);
+ }
+ if (hBar != null) {
+ hBar.setMaximum (0);
+ hBar.setThumb (0);
+ hBar.setSelection(0);
+ }
+ content.setLocation(0, 0);
+ layout();
+ this.content.addListener(SWT.Resize, contentListener);
+ } else {
+ if (hBar != null) hBar.setVisible(alwaysShowScroll);
+ if (vBar != null) vBar.setVisible(alwaysShowScroll);
+ }
+}
+/**
+ * Configure the ScrolledComposite to resize the content object to be as wide as the
+ * ScrolledComposite when the width of the ScrolledComposite is greater than the
+ * minimum width specified in setMinWidth. If the ScrolledComposite is less than the
+ * minimum width, the content will not resized and instead the horizontal scroll bar will be
+ * used to view the entire width.
+ * If expand is false, this behaviour is turned off. By default, this behaviour is turned off.
+ *
+ * @param expand true to expand the content control to fill available horizontal space
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setExpandHorizontal(boolean expand) {
+ checkWidget();
+ if (expand == expandHorizontal) return;
+ expandHorizontal = expand;
+ layout();
+}
+/**
+ * Configure the ScrolledComposite to resize the content object to be as tall as the
+ * ScrolledComposite when the height of the ScrolledComposite is greater than the
+ * minimum height specified in setMinHeight. If the ScrolledComposite is less than the
+ * minimum height, the content will not resized and instead the vertical scroll bar will be
+ * used to view the entire height.
+ * If expand is false, this behaviour is turned off. By default, this behaviour is turned off.
+ *
+ * @param expand true to expand the content control to fill available vertical space
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setExpandVertical(boolean expand) {
+ checkWidget();
+ if (expand == expandVertical) return;
+ expandVertical = expand;
+ layout();
+}
+public void setLayout (Layout layout) {
+ // do not allow a layout to be set on this class because layout is being handled by the resize listener
+ checkWidget();
+ return;
+}
+/**
+ * Specify the minimum height at which the ScrolledComposite will begin scrolling the
+ * content with the vertical scroll bar. This value is only relevant if
+ * setExpandVertical(true) has been set.
+ *
+ * @param height the minimum height or 0 for default height
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinHeight(int height) {
+ setMinSize(minWidth, height);
+}
+/**
+ * Specify the minimum width and height at which the ScrolledComposite will begin scrolling the
+ * content with the horizontal scroll bar. This value is only relevant if
+ * setExpandHorizontal(true) and setExpandVertical(true) have been set.
+ *
+ * @param size the minimum size or null for the default size
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinSize(Point size) {
+ if (size == null) {
+ setMinSize(0, 0);
+ } else {
+ setMinSize(size.x, size.y);
+ }
+}
+/**
+ * Specify the minimum width and height at which the ScrolledComposite will begin scrolling the
+ * content with the horizontal scroll bar. This value is only relevant if
+ * setExpandHorizontal(true) and setExpandVertical(true) have been set.
+ *
+ * @param width the minimum width or 0 for default width
+ * @param height the minimum height or 0 for default height
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinSize(int width, int height) {
+ checkWidget();
+ if (width == minWidth && height == minHeight) return;
+ minWidth = Math.max(0, width);
+ minHeight = Math.max(0, height);
+ layout();
+}
+/**
+ * Specify the minimum width at which the ScrolledComposite will begin scrolling the
+ * content with the horizontal scroll bar. This value is only relevant if
+ * setExpandHorizontal(true) has been set.
+ *
+ * @param width the minimum width or 0 for default width
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinWidth(int width) {
+ setMinSize(width, minHeight);
+}
+
+private void vScroll() {
+ if (content == null) return;
+ Point location = content.getLocation ();
+ ScrollBar vBar = getVerticalBar ();
+ int vSelection = vBar.getSelection ();
+ content.setLocation (location.x, -vSelection);
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StackLayout.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StackLayout.java
index 9a0be76824..4ad2155a2e 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StackLayout.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StackLayout.java
@@ -1,114 +1,114 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * This Layout stacks all the controls one on top of the other and resizes all controls
- * to have the same size and location.
- * The control specified in topControl is visible and all other controls are not visible.
- * Users must set the topControl value to flip between the visible items and the call
- * layout() on the composite which has the StackLayout.
- *
- * <p> Here is an example which places ten buttons in a stack layout and
- * flips between them:
- *
- * <pre><code>
- * public static void main(String[] args) {
- * Display display = new Display();
- * Shell shell = new Shell(display);
- * shell.setLayout(new GridLayout());
- *
- * final Composite parent = new Composite(shell, SWT.NONE);
- * parent.setLayoutData(new GridData(GridData.FILL_BOTH));
- * final StackLayout layout = new StackLayout();
- * parent.setLayout(layout);
- * final Button[] bArray = new Button[10];
- * for (int i = 0; i < 10; i++) {
- * bArray[i] = new Button(parent, SWT.PUSH);
- * bArray[i].setText("Button "+i);
- * }
- * layout.topControl = bArray[0];
- *
- * Button b = new Button(shell, SWT.PUSH);
- * b.setText("Show Next Button");
- * final int[] index = new int[1];
- * b.addListener(SWT.Selection, new Listener(){
- * public void handleEvent(Event e) {
- * index[0] = (index[0] + 1) % 10;
- * layout.topControl = bArray[index[0]];
- * parent.layout();
- * }
- * });
- *
- * shell.open();
- * while (shell != null && !shell.isDisposed()) {
- * if (!display.readAndDispatch())
- * display.sleep();
- * }
- * }
- * </code></pre>
- */
-
-public class StackLayout extends Layout {
-
- /**
- * marginWidth specifies the number of pixels of horizontal margin
- * that will be placed along the left and right edges of the layout.
- *
- * The default value is 0.
- */
- public int marginWidth = 0;
- /**
- * marginHeight specifies the number of pixels of vertical margin
- * that will be placed along the top and bottom edges of the layout.
- *
- * The default value is 0.
- */
- public int marginHeight = 0;
-
- /**
- * topControl the Control that is displayed at the top of the stack.
- * All other controls that are children of the parent composite will not be visible.
- */
- public Control topControl;
-
-protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
- Control children[] = composite.getChildren();
-
- int maxWidth = 0;
- int maxHeight = 0;
- for (int i = 0; i < children.length; i++) {
- Point size = children[i].computeSize(wHint, hHint, flushCache);
- maxWidth = Math.max(size.x, maxWidth);
- maxHeight = Math.max(size.y, maxHeight);
- }
-
- int width = wHint, height = hHint;
- if (wHint == SWT.DEFAULT) width = maxWidth;
- if (hHint == SWT.DEFAULT) height = maxHeight;
- return new Point(width + 2 * marginWidth, height + 2 * marginHeight);
-}
-
-protected void layout(Composite composite, boolean flushCache) {
- Control children[] = composite.getChildren();
- Rectangle rect = composite.getClientArea();
- rect.x += marginWidth;
- rect.y += marginHeight;
- rect.width -= 2 * marginWidth;
- rect.height -= 2 * marginHeight;
- for (int i = 0; i < children.length; i++) {
- children[i].setBounds(rect);
- children[i].setVisible(children[i] == topControl);
-
- }
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * This Layout stacks all the controls one on top of the other and resizes all controls
+ * to have the same size and location.
+ * The control specified in topControl is visible and all other controls are not visible.
+ * Users must set the topControl value to flip between the visible items and the call
+ * layout() on the composite which has the StackLayout.
+ *
+ * <p> Here is an example which places ten buttons in a stack layout and
+ * flips between them:
+ *
+ * <pre><code>
+ * public static void main(String[] args) {
+ * Display display = new Display();
+ * Shell shell = new Shell(display);
+ * shell.setLayout(new GridLayout());
+ *
+ * final Composite parent = new Composite(shell, SWT.NONE);
+ * parent.setLayoutData(new GridData(GridData.FILL_BOTH));
+ * final StackLayout layout = new StackLayout();
+ * parent.setLayout(layout);
+ * final Button[] bArray = new Button[10];
+ * for (int i = 0; i < 10; i++) {
+ * bArray[i] = new Button(parent, SWT.PUSH);
+ * bArray[i].setText("Button "+i);
+ * }
+ * layout.topControl = bArray[0];
+ *
+ * Button b = new Button(shell, SWT.PUSH);
+ * b.setText("Show Next Button");
+ * final int[] index = new int[1];
+ * b.addListener(SWT.Selection, new Listener(){
+ * public void handleEvent(Event e) {
+ * index[0] = (index[0] + 1) % 10;
+ * layout.topControl = bArray[index[0]];
+ * parent.layout();
+ * }
+ * });
+ *
+ * shell.open();
+ * while (shell != null && !shell.isDisposed()) {
+ * if (!display.readAndDispatch())
+ * display.sleep();
+ * }
+ * }
+ * </code></pre>
+ */
+
+public class StackLayout extends Layout {
+
+ /**
+ * marginWidth specifies the number of pixels of horizontal margin
+ * that will be placed along the left and right edges of the layout.
+ *
+ * The default value is 0.
+ */
+ public int marginWidth = 0;
+ /**
+ * marginHeight specifies the number of pixels of vertical margin
+ * that will be placed along the top and bottom edges of the layout.
+ *
+ * The default value is 0.
+ */
+ public int marginHeight = 0;
+
+ /**
+ * topControl the Control that is displayed at the top of the stack.
+ * All other controls that are children of the parent composite will not be visible.
+ */
+ public Control topControl;
+
+protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
+ Control children[] = composite.getChildren();
+
+ int maxWidth = 0;
+ int maxHeight = 0;
+ for (int i = 0; i < children.length; i++) {
+ Point size = children[i].computeSize(wHint, hHint, flushCache);
+ maxWidth = Math.max(size.x, maxWidth);
+ maxHeight = Math.max(size.y, maxHeight);
+ }
+
+ int width = wHint, height = hHint;
+ if (wHint == SWT.DEFAULT) width = maxWidth;
+ if (hHint == SWT.DEFAULT) height = maxHeight;
+ return new Point(width + 2 * marginWidth, height + 2 * marginHeight);
+}
+
+protected void layout(Composite composite, boolean flushCache) {
+ Control children[] = composite.getChildren();
+ Rectangle rect = composite.getClientArea();
+ rect.x += marginWidth;
+ rect.y += marginHeight;
+ rect.width -= 2 * marginWidth;
+ rect.height -= 2 * marginHeight;
+ for (int i = 0; i < children.length; i++) {
+ children[i].setBounds(rect);
+ children[i].setVisible(children[i] == topControl);
+
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyleRange.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyleRange.java
index 7d9643cd3b..887573c8ee 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyleRange.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyleRange.java
@@ -1,155 +1,155 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2003 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.internal.CloneableCompatibility;
-
-public class StyleRange implements CloneableCompatibility {
- public int start; // style start offset. 0 based from the document start
- public int length; // style length.
- public Color foreground;
- public Color background;
- public int fontStyle = SWT.NORMAL; // may be SWT.NORMAL or SWT.BOLD
-
-public StyleRange() {
-}
-/**
- * Create a new style range.
- * <p>
- *
- * @param start start offset of the style
- * @param length length of the style
- * @param foreground foreground color of the style, null if none
- * @param background background color of the style, null if none
- */
-public StyleRange(int start, int length, Color foreground, Color background) {
- this.start = start;
- this.length = length;
- this.foreground = foreground;
- this.background = background;
-}
-
-/**
- * Create a new style range.
- * <p>
- *
- * @param start start offset of the style
- * @param length length of the style
- * @param foreground foreground color of the style, null if none
- * @param background background color of the style, null if none
- * @param fontStyle font style of the style, may be SWT.NORMAL or SWT.BOLD
- */
-public StyleRange(int start, int length, Color foreground, Color background, int fontStyle) {
- this.start = start;
- this.length = length;
- this.foreground = foreground;
- this.background = background;
- this.fontStyle = fontStyle;
-}
-
-/**
- * Compare the specified object to this StyleRange and answer if the two
- * are equal. The object must be an instance of StyleRange and have the
- * same field values.
- * <p>
- *
- * @param object the object to compare with this object
- * @return true if the objects are equal, false otherwise
- */
-public boolean equals(Object object) {
- StyleRange style;
- if (object == this) return true;
- if (object instanceof StyleRange) style = (StyleRange)object;
- else return false;
- if (this.start != style.start) return false;
- if (this.length != style.length) return false;
- if (this.foreground != null) {
- if (!this.foreground.equals(style.foreground)) return false;
- } else if (style.foreground != null) return false;
- if (this.background != null) {
- if (!this.background.equals(style.background)) return false;
- } else if (style.background != null) return false;
- if (this.fontStyle != style.fontStyle) return false;
- return true;
-}
-/**
- * Returns an integer hash code for the receiver. Objects which are
- * equal answer the same value for this method.
- * <p>
- *
- * @return the receiver's hash
- */
-public int hashCode() {
- int code = start + length;
-
- if (foreground != null)
- code += foreground.hashCode();
- if (background != null)
- code += background.hashCode();
- return code + fontStyle;
-}
-/**
- * Returns whether or not the receiver is unstyled (i.e., does not have any
- * style attributes specified).
- * <p>
- *
- * @return true if the receiver is unstyled, false otherwise.
- */
-public boolean isUnstyled() {
- if (this.foreground != null) return false;
- if (this.background != null) return false;
- if (this.fontStyle != SWT.NORMAL) return false;
- return true;
-}
-/**
- * Compares the specified object to this StyleRange and answer if the two
- * are similar. The object must be an instance of StyleRange and have the
- * same field values for except for start and length.
- * <p>
- *
- * @param object the object to compare with this object
- * @return true if the objects are similar, false otherwise
- */
-public boolean similarTo(StyleRange style) {
- if (this.foreground != null) {
- if (!this.foreground.equals(style.foreground)) return false;
- } else if (style.foreground != null) return false;
- if (this.background != null) {
- if (!this.background.equals(style.background)) return false;
- } else if (style.background != null) return false;
- if (this.fontStyle != style.fontStyle) return false;
- return true;
-}
-/**
- * Answers a new StyleRange with the same values as this StyleRange.
- * <p>
- *
- * @return a shallow copy of this StyleRange
- */
-public Object clone() {
- StyleRange style = new StyleRange(start, length, foreground, background, fontStyle);
- return style;
-}
-/**
- * Answers a string description of the receiver.
- * <p>
- *
- * @return a printable representation for the receiver.
- */
-public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append(start + "," + length + " fg:" + foreground + " bg:" + background + " fStyle:");
- if (fontStyle == SWT.NORMAL) {
- buf.append("normal");
- } else if (fontStyle == SWT.BOLD) {
- buf.append("bold");
- }
- return buf.toString();
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.CloneableCompatibility;
+
+public class StyleRange implements CloneableCompatibility {
+ public int start; // style start offset. 0 based from the document start
+ public int length; // style length.
+ public Color foreground;
+ public Color background;
+ public int fontStyle = SWT.NORMAL; // may be SWT.NORMAL or SWT.BOLD
+
+public StyleRange() {
+}
+/**
+ * Create a new style range.
+ * <p>
+ *
+ * @param start start offset of the style
+ * @param length length of the style
+ * @param foreground foreground color of the style, null if none
+ * @param background background color of the style, null if none
+ */
+public StyleRange(int start, int length, Color foreground, Color background) {
+ this.start = start;
+ this.length = length;
+ this.foreground = foreground;
+ this.background = background;
+}
+
+/**
+ * Create a new style range.
+ * <p>
+ *
+ * @param start start offset of the style
+ * @param length length of the style
+ * @param foreground foreground color of the style, null if none
+ * @param background background color of the style, null if none
+ * @param fontStyle font style of the style, may be SWT.NORMAL or SWT.BOLD
+ */
+public StyleRange(int start, int length, Color foreground, Color background, int fontStyle) {
+ this.start = start;
+ this.length = length;
+ this.foreground = foreground;
+ this.background = background;
+ this.fontStyle = fontStyle;
+}
+
+/**
+ * Compare the specified object to this StyleRange and answer if the two
+ * are equal. The object must be an instance of StyleRange and have the
+ * same field values.
+ * <p>
+ *
+ * @param object the object to compare with this object
+ * @return true if the objects are equal, false otherwise
+ */
+public boolean equals(Object object) {
+ StyleRange style;
+ if (object == this) return true;
+ if (object instanceof StyleRange) style = (StyleRange)object;
+ else return false;
+ if (this.start != style.start) return false;
+ if (this.length != style.length) return false;
+ if (this.foreground != null) {
+ if (!this.foreground.equals(style.foreground)) return false;
+ } else if (style.foreground != null) return false;
+ if (this.background != null) {
+ if (!this.background.equals(style.background)) return false;
+ } else if (style.background != null) return false;
+ if (this.fontStyle != style.fontStyle) return false;
+ return true;
+}
+/**
+ * Returns an integer hash code for the receiver. Objects which are
+ * equal answer the same value for this method.
+ * <p>
+ *
+ * @return the receiver's hash
+ */
+public int hashCode() {
+ int code = start + length;
+
+ if (foreground != null)
+ code += foreground.hashCode();
+ if (background != null)
+ code += background.hashCode();
+ return code + fontStyle;
+}
+/**
+ * Returns whether or not the receiver is unstyled (i.e., does not have any
+ * style attributes specified).
+ * <p>
+ *
+ * @return true if the receiver is unstyled, false otherwise.
+ */
+public boolean isUnstyled() {
+ if (this.foreground != null) return false;
+ if (this.background != null) return false;
+ if (this.fontStyle != SWT.NORMAL) return false;
+ return true;
+}
+/**
+ * Compares the specified object to this StyleRange and answer if the two
+ * are similar. The object must be an instance of StyleRange and have the
+ * same field values for except for start and length.
+ * <p>
+ *
+ * @param object the object to compare with this object
+ * @return true if the objects are similar, false otherwise
+ */
+public boolean similarTo(StyleRange style) {
+ if (this.foreground != null) {
+ if (!this.foreground.equals(style.foreground)) return false;
+ } else if (style.foreground != null) return false;
+ if (this.background != null) {
+ if (!this.background.equals(style.background)) return false;
+ } else if (style.background != null) return false;
+ if (this.fontStyle != style.fontStyle) return false;
+ return true;
+}
+/**
+ * Answers a new StyleRange with the same values as this StyleRange.
+ * <p>
+ *
+ * @return a shallow copy of this StyleRange
+ */
+public Object clone() {
+ StyleRange style = new StyleRange(start, length, foreground, background, fontStyle);
+ return style;
+}
+/**
+ * Answers a string description of the receiver.
+ * <p>
+ *
+ * @return a printable representation for the receiver.
+ */
+public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append(start + "," + length + " fg:" + foreground + " bg:" + background + " fStyle:");
+ if (fontStyle == SWT.NORMAL) {
+ buf.append("normal");
+ } else if (fontStyle == SWT.BOLD) {
+ buf.append("bold");
+ }
+ return buf.toString();
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java
index c629ce1054..31dd608a06 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java
@@ -1,8267 +1,8267 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2003 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import java.util.*;
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.dnd.*;
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.internal.*;
-import org.eclipse.swt.printing.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * A StyledText is an editable user interface object that displays lines
- * of text. The following style attributes can be defined for the text:
- * <ul>
- * <li>foreground color
- * <li>background color
- * <li>font style (bold, regular)
- * </ul>
- * <p>
- * In addition to text style attributes, the background color of a line may
- * 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
- * not define your own listener and use the StyledText API. The following
- * StyledText API is not supported if you have defined a LineStyleListener:
- * <ul>
- * <li>getStyleRangeAtOffset(int)
- * <li>getStyleRanges()
- * <li>replaceStyleRanges(int,int,StyleRange[])
- * <li>setStyleRange(StyleRange)
- * <li>setStyleRanges(StyleRange[])
- * </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
- * LineBackgroundListener:
- * <ul>
- * <li>getLineBackground(int)
- * <li>setLineBackground(int,int,Color)
- * </ul>
- * </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.
- * </p>
- * <p>
- * IMPORTANT: This class is <em>not</em> intended to be subclassed.
- * </p>
- * <dl>
- * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
- * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetSegments, LineGetStyle, Modify, Selection, Verify, VerifyKey
- * </dl>
- */
-public class StyledText extends Canvas {
- static final char TAB = '\t';
- static final String PlatformLineDelimiter = System.getProperty("line.separator");
- static final int BIDI_CARET_WIDTH = 4;
- static final int XINSET = BIDI_CARET_WIDTH - 1;
- static final int DEFAULT_WIDTH = 64;
- static final int DEFAULT_HEIGHT = 64;
-
- static final int ExtendedModify = 3000;
- static final int LineGetBackground = 3001;
- static final int LineGetStyle = 3002;
- static final int TextChanging = 3003;
- static final int TextSet = 3004;
- static final int VerifyKey = 3005;
- static final int TextChanged = 3006;
- static final int LineGetSegments = 3007;
-
- Color selectionBackground; // selection background color
- Color selectionForeground; // selection foreground color
- StyledTextContent logicalContent; // native content (default or user specified)
- StyledTextContent content; // line wrapping content, same as logicalContent if word wrap is off
- DisplayRenderer renderer;
- TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent
- DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered
- LineCache lineCache;
- boolean userLineStyle = false; // true=widget is using a user defined line style listener for line styles. false=widget is using the default line styler to store line styles
- boolean userLineBackground = false; // true=widget is using a user defined line background listener for line backgrounds. false=widget is using the default line styler to store line backgrounds
- int verticalScrollOffset = 0; // pixel based
- int horizontalScrollOffset = 0; // pixel based
- int topIndex = 0; // top visible line
- int topOffset = 0; // offset of first character in top line
- int clientAreaHeight = 0; // the client area height. Needed to calculate content width for new
- // visible lines during Resize callback
- int clientAreaWidth = 0; // the client area width. Needed during Resize callback to determine
- // if line wrap needs to be recalculated
- int lineHeight; // line height=font height
- int tabLength = 4; // number of characters in a tab
- int lineEndSpaceWidth; // space, in pixel, used to indicated a selected line break
- int leftMargin = 1;
- int topMargin = 1;
- int rightMargin = 2;
- int bottomMargin = 2;
- Cursor ibeamCursor;
- int columnX; // keep track of the horizontal caret position
- // when changing lines/pages. Fixes bug 5935
- int caretOffset = 0;
- Point selection = new Point(0, 0); // x is character offset, y is length
- int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text
- Point doubleClickSelection; // selection after last mouse double click
- boolean editable = true;
- boolean wordWrap = false;
- boolean doubleClickEnabled = true; // see getDoubleClickEnabled
- boolean overwrite = false; // insert/overwrite edit mode
- int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default.
- Hashtable keyActionMap = new Hashtable();
- Color background = null; // workaround for bug 4791
- Color foreground = null; //
- Clipboard clipboard;
- boolean mouseDoubleClick = false; // true=a double click ocurred. Don't do mouse swipe selection.
- int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left)
- int lastTextChangeStart; // cache data of the
- int lastTextChangeNewLineCount; // last text changing
- int lastTextChangeNewCharCount; // event for use in the
- int lastTextChangeReplaceLineCount; // text changed handler
- int lastTextChangeReplaceCharCount;
- boolean isBidi;
- boolean bidiColoring = false; // apply the BIDI algorithm on text segments of the same color
- Image leftCaretBitmap = null;
- Image rightCaretBitmap = null;
- int caretDirection = SWT.NULL;
- PaletteData caretPalette = null;
- int lastCaretDirection = SWT.NULL;
- boolean isCarbon; // flag set to true on Mac OSX
-
- /**
- * The Printing class implements printing of a range of text.
- * An instance of <class>Printing </class> is returned in the
- * StyledText#print(Printer) API. The run() method may be
- * invoked from any thread.
- */
- class Printing implements Runnable {
- final static int LEFT = 0; // left aligned header/footer segment
- final static int CENTER = 1; // centered header/footer segment
- final static int RIGHT = 2; // right aligned header/footer segment
-
- Printer printer;
- PrintRenderer renderer;
- StyledTextPrintOptions printOptions;
- StyledTextContent printerContent; // copy of the widget content
- Rectangle clientArea; // client area to print on
- Font printerFont;
- FontData displayFontData;
- Hashtable printerColors; // printer color cache for line backgrounds and style
- Hashtable lineBackgrounds = new Hashtable(); // cached line backgrounds
- Hashtable lineStyles = new Hashtable(); // cached line styles
- Hashtable bidiSegments = new Hashtable(); // cached bidi segments when running on a bidi platform
- GC gc; // printer GC
- int pageWidth; // width of a printer page in pixels
- int startPage; // first page to print
- int endPage; // last page to print
- int pageSize; // number of lines on a page
- int startLine; // first (wrapped) line to print
- int endLine; // last (wrapped) line to print
- boolean singleLine; // widget single line mode
- Point selection = null; // selected text
-
- /**
- * Creates an instance of <class>Printing</class>.
- * Copies the widget content and rendering data that needs
- * to be requested from listeners.
- * </p>
- * @param parent StyledText widget to print.
- * @param printer printer device to print on.
- * @param printOptions print options
- */
- Printing(StyledText parent, Printer printer, StyledTextPrintOptions printOptions) {
- PrinterData data = printer.getPrinterData();
-
- this.printer = printer;
- this.printOptions = printOptions;
- singleLine = parent.isSingleLine();
- startPage = 1;
- endPage = Integer.MAX_VALUE;
- if (data.scope == PrinterData.PAGE_RANGE) {
- startPage = data.startPage;
- endPage = data.endPage;
- if (endPage < startPage) {
- int temp = endPage;
- endPage = startPage;
- startPage = temp;
- }
- } if (data.scope == PrinterData.SELECTION) {
- selection = parent.getSelectionRange();
- }
-
- displayFontData = getFont().getFontData()[0];
- copyContent(parent.getContent());
- cacheLineData(printerContent);
- }
- /**
- * Caches the bidi segments of the given line.
- * </p>
- * @param lineOffset offset of the line to cache bidi segments for.
- * Relative to the start of the document.
- * @param line line to cache bidi segments for.
- */
- void cacheBidiSegments(int lineOffset, String line) {
- int[] segments = getBidiSegments(lineOffset, line);
-
- if (segments != null) {
- bidiSegments.put(new Integer(lineOffset), segments);
- }
- }
- /**
- * Caches the line background color of the given line.
- * </p>
- * @param lineOffset offset of the line to cache the background
- * color for. Relative to the start of the document.
- * @param line line to cache the background color for
- */
- void cacheLineBackground(int lineOffset, String line) {
- StyledTextEvent event = getLineBackgroundData(lineOffset, line);
-
- if (event != null) {
- lineBackgrounds.put(new Integer(lineOffset), event);
- }
- }
- /**
- * Caches all line data that needs to be requested from a listener.
- * </p>
- * @param printerContent <class>StyledTextContent</class> to request
- * line data for.
- */
- void cacheLineData(StyledTextContent printerContent) {
- for (int i = 0; i < printerContent.getLineCount(); i++) {
- int lineOffset = printerContent.getOffsetAtLine(i);
- String line = printerContent.getLine(i);
-
- if (printOptions.printLineBackground) {
- cacheLineBackground(lineOffset, line);
- }
- if (printOptions.printTextBackground ||
- printOptions.printTextForeground ||
- printOptions.printTextFontStyle) {
- cacheLineStyle(lineOffset, line);
- }
- if (isBidi()) {
- cacheBidiSegments(lineOffset, line);
- }
- }
- }
- /**
- * Caches all line styles of the given line.
- * </p>
- * @param lineOffset offset of the line to cache the styles for.
- * Relative to the start of the document.
- * @param line line to cache the styles for.
- */
- void cacheLineStyle(int lineOffset, String line) {
- StyledTextEvent event = getLineStyleData(lineOffset, line);
-
- if (event != null) {
- StyleRange[] styles = event.styles;
- for (int i = 0; i < styles.length; i++) {
- StyleRange styleCopy = null;
- if (printOptions.printTextBackground == false && styles[i].background != null) {
- styleCopy = (StyleRange) styles[i].clone();
- styleCopy.background = null;
- }
- if (printOptions.printTextForeground == false && styles[i].foreground != null) {
- if (styleCopy == null) {
- styleCopy = (StyleRange) styles[i].clone();
- }
- styleCopy.foreground = null;
- }
- if (printOptions.printTextFontStyle == false && styles[i].fontStyle != SWT.NORMAL) {
- if (styleCopy == null) {
- styleCopy = (StyleRange) styles[i].clone();
- }
- styleCopy.fontStyle = SWT.NORMAL;
- }
- if (styleCopy != null) {
- styles[i] = styleCopy;
- }
- }
- lineStyles.put(new Integer(lineOffset), event);
- }
- }
- /**
- * Copies the text of the specified <class>StyledTextContent</class>.
- * </p>
- * @param original the <class>StyledTextContent</class> to copy.
- */
- void copyContent(StyledTextContent original) {
- int insertOffset = 0;
-
- printerContent = new DefaultContent();
- for (int i = 0; i < original.getLineCount(); i++) {
- int insertEndOffset;
- if (i < original.getLineCount() - 1) {
- insertEndOffset = original.getOffsetAtLine(i + 1);
- }
- else {
- insertEndOffset = original.getCharCount();
- }
- printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
- insertOffset = insertEndOffset;
- }
- }
- /**
- * Replaces all display colors in the cached line backgrounds and
- * line styles with printer colors.
- */
- void createPrinterColors() {
- Enumeration values = lineBackgrounds.elements();
- printerColors = new Hashtable();
- while (values.hasMoreElements()) {
- StyledTextEvent event = (StyledTextEvent) values.nextElement();
- event.lineBackground = getPrinterColor(event.lineBackground);
- }
-
- values = lineStyles.elements();
- while (values.hasMoreElements()) {
- StyledTextEvent event = (StyledTextEvent) values.nextElement();
- for (int i = 0; i < event.styles.length; i++) {
- StyleRange style = event.styles[i];
- Color printerBackground = getPrinterColor(style.background);
- Color printerForeground = getPrinterColor(style.foreground);
-
- if (printerBackground != style.background ||
- printerForeground != style.foreground) {
- style = (StyleRange) style.clone();
- style.background = printerBackground;
- style.foreground = printerForeground;
- event.styles[i] = style;
- }
- }
- }
- }
- /**
- * Disposes of the resources and the <class>PrintRenderer</class>.
- */
- void dispose() {
- if (printerColors != null) {
- Enumeration colors = printerColors.elements();
-
- while (colors.hasMoreElements()) {
- Color color = (Color) colors.nextElement();
- color.dispose();
- }
- printerColors = null;
- }
- if (gc != null) {
- gc.dispose();
- gc = null;
- }
- if (printerFont != null) {
- printerFont.dispose();
- printerFont = null;
- }
- if (renderer != null) {
- renderer.dispose();
- renderer = null;
- }
- }
- /**
- * Finish printing the indicated page.
- *
- * @param page page that was printed
- */
- void endPage(int page) {
- printDecoration(page, false);
- printer.endPage();
- }
- /**
- * Creates a <class>PrintRenderer</class> and calculate the line range
- * to print.
- */
- void initializeRenderer() {
- Rectangle trim = printer.computeTrim(0, 0, 0, 0);
- Point dpi = printer.getDPI();
-
- printerFont = new Font(printer, displayFontData.getName(), displayFontData.getHeight(), SWT.NORMAL);
- clientArea = printer.getClientArea();
- pageWidth = clientArea.width;
- // one inch margin around text
- clientArea.x = dpi.x + trim.x;
- clientArea.y = dpi.y + trim.y;
- clientArea.width -= (clientArea.x + trim.width);
- clientArea.height -= (clientArea.y + trim.height);
-
- gc = new GC(printer);
- gc.setFont(printerFont);
- renderer = new PrintRenderer(
- printer, printerFont, isBidi(), gc, printerContent,
- lineBackgrounds, lineStyles, bidiSegments,
- tabLength, clientArea);
- if (printOptions.header != null) {
- int lineHeight = renderer.getLineHeight();
- clientArea.y += lineHeight * 2;
- clientArea.height -= lineHeight * 2;
- }
- if (printOptions.footer != null) {
- clientArea.height -= renderer.getLineHeight() * 2;
- }
- pageSize = clientArea.height / renderer.getLineHeight();
- StyledTextContent content = renderer.getContent();
- startLine = 0;
- endLine = content.getLineCount() - 1;
- PrinterData data = printer.getPrinterData();
- if (data.scope == PrinterData.PAGE_RANGE) {
- startLine = (startPage - 1) * pageSize;
- } else if (data.scope == PrinterData.SELECTION) {
- startLine = content.getLineAtOffset(selection.x);
- if (selection.y > 0) {
- endLine = content.getLineAtOffset(selection.x + selection.y - 1);
- } else {
- endLine = startLine - 1;
- }
- }
- }
- /**
- * Returns the printer color for the given display color.
- * </p>
- * @param color display color
- * @return color create on the printer with the same RGB values
- * as the display color.
- */
- Color getPrinterColor(Color color) {
- Color printerColor = null;
-
- if (color != null) {
- printerColor = (Color) printerColors.get(color);
- if (printerColor == null) {
- printerColor = new Color(printer, color.getRGB());
- printerColors.put(color, printerColor);
- }
- }
- return printerColor;
- }
- /**
- * Prints the lines in the specified page range.
- */
- void print() {
- StyledTextContent content = renderer.getContent();
- Color background = gc.getBackground();
- Color foreground = gc.getForeground();
- int lineHeight = renderer.getLineHeight();
- int lineCount = content.getLineCount();
- int paintY = clientArea.y;
- int page = startPage;
-
- if (singleLine) {
- lineCount = 1;
- }
- for (int i = startLine; i <= endLine && page <= endPage; i++, paintY += lineHeight) {
- String line = content.getLine(i);
-
- if (paintY == clientArea.y) {
- startPage(page);
- }
- renderer.drawLine(
- line, i, paintY, gc, background, foreground, true);
- if (paintY + lineHeight * 2 > clientArea.y + clientArea.height) {
- endPage(page);
- paintY = clientArea.y;
- page++;
- if (page > endPage || i == lineCount - 1) {
- break;
- }
- }
- }
- if (paintY > clientArea.y && paintY <= clientArea.y + clientArea.height) {
- endPage(page);
- }
- }
- /**
- * Print header or footer decorations.
- * * @param page page number to print, if specified in the StyledTextPrintOptions header or footer. * @param header true = print the header, false = print the footer */
- void printDecoration(int page, boolean header) {
- int lastSegmentIndex = 0;
- final int SegmentCount = 3;
- String text;
-
- if (header) {
- text = printOptions.header;
- }
- else {
- text = printOptions.footer;
- }
- if (text == null) {
- return;
- }
- for (int i = 0; i < SegmentCount; i++) {
- int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
- String segment;
-
- if (segmentIndex == -1) {
- segment = text.substring(lastSegmentIndex);
- printDecorationSegment(segment, i, page, header);
- break;
- }
- else {
- segment = text.substring(lastSegmentIndex, segmentIndex);
- printDecorationSegment(segment, i, page, header);
- lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
- }
- }
- }
- /**
- * Print one segment of a header or footer decoration.
- * Headers and footers have three different segments.
- * One each for left aligned, centered, and right aligned text.
- *
- * @param segment decoration segment to print
- * @param alignment alignment of the segment. 0=left, 1=center, 2=right
- * @param page page number to print, if specified in the decoration segment.
- * @param header true = print the header, false = print the footer
- */
- void printDecorationSegment(String segment, int alignment, int page, boolean header) {
- int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
-
- if (pageIndex != -1) {
- final int PageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
- StringBuffer buffer = new StringBuffer(segment);
- buffer.replace(pageIndex, pageIndex + PageTagLength, new Integer(page).toString());
- segment = buffer.toString();
- }
- if (segment.length() > 0) {
- int segmentWidth;
- int drawX = 0;
- int drawY;
- StyledTextBidi bidi = null;
-
- if (isBidi()) {
- bidi = new StyledTextBidi(gc, tabLength, segment, null, null, new int[] {0, segment.length()});
- segmentWidth = bidi.getTextWidth();
- }
- else {
- segmentWidth = gc.textExtent(segment).x;
- }
- if (header) {
- drawY = clientArea.y - renderer.getLineHeight() * 2;
- }
- else {
- drawY = clientArea.y + clientArea.height + renderer.getLineHeight();
- }
- if (alignment == LEFT) {
- drawX = clientArea.x;
- }
- else
- if (alignment == CENTER) {
- drawX = (pageWidth - segmentWidth) / 2;
- }
- else
- if (alignment == RIGHT) {
- drawX = clientArea.x + clientArea.width - segmentWidth;
- }
- if (bidi != null) {
- bidi.drawBidiText(0, segment.length(), drawX, drawY);
- }
- else {
- gc.drawString(segment, drawX, drawY, true);
- }
- }
- }
- /**
- * Starts a print job and prints the pages specified in the constructor.
- */
- public void run() {
- String jobName = printOptions.jobName;
-
- if (jobName == null) {
- jobName = "Printing";
- }
- if (printer.startJob(jobName)) {
- createPrinterColors();
- initializeRenderer();
- print();
- dispose();
- printer.endJob();
- }
- }
- /**
- * Start printing a new page.
- *
- * @param page page number to be started
- */
- void startPage(int page) {
- printer.startPage();
- printDecoration(page, true);
- }
- }
- /**
- * The <code>RTFWriter</code> class is used to write widget content as
- * rich text. The implementation complies with the RTF specification
- * version 1.5.
- * <p>
- * toString() is guaranteed to return a valid RTF string only after
- * close() has been called.
- * </p>
- * <p>
- * Whole and partial lines and line breaks can be written. Lines will be
- * formatted using the styles queried from the LineStyleListener, if
- * set, or those set directly in the widget. All styles are applied to
- * the RTF stream like they are rendered by the widget. In addition, the
- * widget font name and size is used for the whole text.
- * </p>
- */
- class RTFWriter extends TextWriter {
- final int DEFAULT_FOREGROUND = 0;
- final int DEFAULT_BACKGROUND = 1;
- Vector colorTable = new Vector();
- boolean WriteUnicode;
-
- /**
- * Creates a RTF writer that writes content starting at offset "start"
- * in the document. <code>start</code> and <code>length</code>can be set to specify partial
- * lines.
- * <p>
- *
- * @param start start offset of content to write, 0 based from
- * beginning of document
- * @param length length of content to write
- */
- public RTFWriter(int start, int length) {
- super(start, length);
- colorTable.addElement(getForeground());
- colorTable.addElement(getBackground());
- setUnicode();
- }
- /**
- * Closes the RTF writer. Once closed no more content can be written.
- * <b>NOTE:</b> <code>toString()</code> does not return a valid RTF string until
- * <code>close()</code> has been called.
- */
- public void close() {
- if (isClosed() == false) {
- writeHeader();
- write("\n}}\0");
- super.close();
- }
- }
- /**
- * Returns the index of the specified color in the RTF color table.
- * <p>
- *
- * @param color the color
- * @param defaultIndex return value if color is null
- * @return the index of the specified color in the RTF color table
- * or "defaultIndex" if "color" is null.
- */
- int getColorIndex(Color color, int defaultIndex) {
- int index;
-
- if (color == null) {
- index = defaultIndex;
- }
- else {
- index = colorTable.indexOf(color);
- if (index == -1) {
- index = colorTable.size();
- colorTable.addElement(color);
- }
- }
- return index;
- }
- /**
- * Determines if Unicode RTF should be written.
- * Don't write Unicode RTF on Windows 95/98/ME or NT.
- */
- void setUnicode() {
- final String Win95 = "windows 95";
- final String Win98 = "windows 98";
- final String WinME = "windows me";
- final String WinNT = "windows nt";
- String osName = System.getProperty("os.name").toLowerCase();
- String osVersion = System.getProperty("os.version");
- int majorVersion = 0;
-
- if (osName.startsWith(WinNT) && osVersion != null) {
- int majorIndex = osVersion.indexOf('.');
- if (majorIndex != -1) {
- osVersion = osVersion.substring(0, majorIndex);
- try {
- majorVersion = Integer.parseInt(osVersion);
- }
- catch (NumberFormatException exception) {
- // ignore exception. version number remains unknown.
- // will write without Unicode
- }
- }
- }
- if (osName != null &&
- osName.startsWith(Win95) == false &&
- osName.startsWith(Win98) == false &&
- osName.startsWith(WinME) == false &&
- (osName.startsWith(WinNT) == false || majorVersion > 4)) {
- WriteUnicode = true;
- }
- else {
- WriteUnicode = false;
- }
- }
- /**
- * Appends the specified segment of "string" to the RTF data.
- * Copy from <code>start</code> up to, but excluding, <code>end</code>.
- * <p>
- *
- * @param string string to copy a segment from. Must not contain
- * line breaks. Line breaks should be written using writeLineDelimiter()
- * @param start start offset of segment. 0 based.
- * @param end end offset of segment
- */
- void write(String string, int start, int end) {
- for (int index = start; index < end; index++) {
- char ch = string.charAt(index);
- if (ch > 0xFF && WriteUnicode) {
- // write the sub string from the last escaped character
- // to the current one. Fixes bug 21698.
- if (index > start) {
- write(string.substring(start, index));
- }
- write("\\u");
- write(Integer.toString((short) ch));
- write(' '); // control word delimiter
- start = index + 1;
- }
- else
- if (ch == '}' || ch == '{' || ch == '\\') {
- // write the sub string from the last escaped character
- // to the current one. Fixes bug 21698.
- if (index > start) {
- write(string.substring(start, index));
- }
- write('\\');
- write(ch);
- start = index + 1;
- }
- }
- // write from the last escaped character to the end.
- // Fixes bug 21698.
- if (start < end) {
- write(string.substring(start, end));
- }
- }
- /**
- * Writes the RTF header including font table and color table.
- */
- void writeHeader() {
- StringBuffer header = new StringBuffer();
- FontData fontData = getFont().getFontData()[0];
- header.append("{\\rtf1\\ansi");
- // specify code page, necessary for copy to work in bidi
- // systems that don't support Unicode RTF.
- String cpg = System.getProperty("file.encoding").toLowerCase();
- if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
- cpg = cpg.substring(2, cpg.length());
- header.append("\\ansicpg");
- header.append(cpg);
- }
- header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
- header.append(fontData.getName());
- header.append(";}}\n{\\colortbl");
- for (int i = 0; i < colorTable.size(); i++) {
- Color color = (Color) colorTable.elementAt(i);
- header.append("\\red");
- header.append(color.getRed());
- header.append("\\green");
- header.append(color.getGreen());
- header.append("\\blue");
- header.append(color.getBlue());
- header.append(";");
- }
- // some RTF readers ignore the deff0 font tag. Explicitly
- // set the font for the whole document to work around this.
- header.append("}\n{\\f0\\fs");
- // font size is specified in half points
- header.append(fontData.getHeight() * 2);
- header.append(" ");
- write(header.toString(), 0);
- }
- /**
- * Appends the specified line text to the RTF data. Lines will be formatted
- * using the styles queried from the LineStyleListener, if set, or those set
- * directly in the widget.
- * <p>
- *
- * @param line line text to write as RTF. Must not contain line breaks
- * Line breaks should be written using writeLineDelimiter()
- * @param lineOffset offset of the line. 0 based from the start of the
- * widget document. Any text occurring before the start offset or after the
- * end offset specified during object creation is ignored.
- * @exception SWTException <ul>
- * <li>ERROR_IO when the writer is closed.</li>
- * </ul>
- */
- public void writeLine(String line, int lineOffset) {
- StyleRange[] styles = new StyleRange[0];
- Color lineBackground = null;
- StyledTextEvent event;
-
- if (isClosed()) {
- SWT.error(SWT.ERROR_IO);
- }
- event = renderer.getLineStyleData(lineOffset, line);
- if (event != null) {
- styles = event.styles;
- }
- event = renderer.getLineBackgroundData(lineOffset, line);
- if (event != null) {
- lineBackground = event.lineBackground;
- }
- if (lineBackground == null) {
- lineBackground = getBackground();
- }
- writeStyledLine(line, lineOffset, styles, lineBackground);
- }
- /**
- * Appends the specified line delmimiter to the RTF data.
- * <p>
- *
- * @param lineDelimiter line delimiter to write as RTF.
- * @exception SWTException <ul>
- * <li>ERROR_IO when the writer is closed.</li>
- * </ul>
- */
- public void writeLineDelimiter(String lineDelimiter) {
- if (isClosed()) {
- SWT.error(SWT.ERROR_IO);
- }
- write(lineDelimiter, 0, lineDelimiter.length());
- write("\\par ");
- }
- /**
- * Appends the specified line text to the RTF data.
- * Use the colors and font styles specified in "styles" and "lineBackground".
- * Formatting is written to reflect the text rendering by the text widget.
- * Style background colors take precedence over the line background color.
- * Background colors are written using the \highlight tag (vs. the \cb tag).
- * <p>
- *
- * @param line line text to write as RTF. Must not contain line breaks
- * Line breaks should be written using writeLineDelimiter()
- * @param lineOffset offset of the line. 0 based from the start of the
- * widget document. Any text occurring before the start offset or after the
- * end offset specified during object creation is ignored.
- * @param styles styles to use for formatting. Must not be null.
- * @param linebackground line background color to use for formatting.
- * May be null.
- */
- void writeStyledLine(String line, int lineOffset, StyleRange[] styles, Color lineBackground) {
- int lineLength = line.length();
- int lineIndex;
- int copyEnd;
- int startOffset = getStart();
- int endOffset = startOffset + super.getCharCount();
- int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
- int writeOffset = startOffset - lineOffset;
-
- if (writeOffset >= line.length()) {
- return; // whole line is outside write range
- }
- else
- if (writeOffset > 0) {
- lineIndex = writeOffset; // line starts before RTF write start
- }
- else {
- lineIndex = 0;
- }
- if (lineBackground != null) {
- write("{\\highlight");
- write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
- write(" ");
- }
- for (int i = 0; i < styles.length; i++) {
- StyleRange style = styles[i];
- int start = style.start - lineOffset;
- int end = start + style.length;
- int colorIndex;
- // skip over partial first line
- if (end < writeOffset) {
- continue;
- }
- // style starts beyond line end or RTF write end
- if (start >= lineEndOffset) {
- break;
- }
- // write any unstyled text
- if (lineIndex < start) {
- // copy to start of style
- // style starting betond end of write range or end of line
- // is guarded against above.
- write(line, lineIndex, start);
- lineIndex = start;
- }
- // write styled text
- colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
- write("{\\cf");
- write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
- if (colorIndex != DEFAULT_BACKGROUND) {
- write("\\highlight");
- write(colorIndex);
- }
- if (style.fontStyle == SWT.BOLD) {
- write("\\b");
- }
- write(" ");
- // copy to end of style or end of write range or end of line
- copyEnd = Math.min(end, lineEndOffset);
- // guard against invalid styles and let style processing continue
- copyEnd = Math.max(copyEnd, lineIndex);
- write(line, lineIndex, copyEnd);
- if (style.fontStyle == SWT.BOLD) {
- write("\\b0");
- }
- write("}");
- lineIndex = copyEnd;
- }
- // write unstyled text at the end of the line
- if (lineIndex < lineEndOffset) {
- write(line, lineIndex, lineEndOffset);
- }
- if (lineBackground != null) {
- write("}");
- }
- }
- }
- /**
- * The <code>TextWriter</code> class is used to write widget content to
- * a string. Whole and partial lines and line breaks can be written. To write
- * partial lines, specify the start and length of the desired segment
- * during object creation.
- * <p>
- * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
- * has been called.
- */
- class TextWriter {
- private StringBuffer buffer;
- private int startOffset; // offset of first character that will be written
- private int endOffset; // offset of last character that will be written.
- // 0 based from the beginning of the widget text.
- private boolean isClosed = false;
-
- /**
- * Creates a writer that writes content starting at offset "start"
- * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines.
- * <p>
- *
- * @param start start offset of content to write, 0 based from beginning of document
- * @param length length of content to write
- */
- public TextWriter(int start, int length) {
- buffer = new StringBuffer(length);
- startOffset = start;
- endOffset = start + length;
- }
- /**
- * Closes the writer. Once closed no more content can be written.
- * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless
- * the writer is closed.
- */
- public void close() {
- if (isClosed == false) {
- isClosed = true;
- }
- }
- /**
- * Returns the number of characters to write.
- */
- public int getCharCount() {
- return endOffset - startOffset;
- }
- /**
- * Returns the offset where writing starts. 0 based from the start of
- * the widget text. Used to write partial lines.
- */
- public int getStart() {
- return startOffset;
- }
- /**
- * Returns whether the writer is closed.
- */
- public boolean isClosed() {
- return isClosed;
- }
- /**
- * Returns the string. <code>close()</code> must be called before <code>toString()</code>
- * is guaranteed to return a valid string.
- * <p>
- *
- * @return the string
- */
- public String toString() {
- return buffer.toString();
- }
- /**
- * Appends the given string to the data.
- */
- void write(String string) {
- buffer.append(string);
- }
- /**
- * Inserts the given string to the data at the specified offset.
- * Do nothing if "offset" is < 0 or > getCharCount()
- * <p>
- *
- * @param string text to insert
- * @param offset offset in the existing data to insert "string" at.
- */
- void write(String string, int offset) {
- if (offset < 0 || offset > buffer.length()) {
- return;
- }
- buffer.insert(offset, string);
- }
- /**
- * Appends the given int to the data.
- */
- void write(int i) {
- buffer.append(i);
- }
- /**
- * Appends the given character to the data.
- */
- void write(char i) {
- buffer.append(i);
- }
- /**
- * Appends the specified line text to the data.
- * <p>
- *
- * @param line line text to write. Must not contain line breaks
- * Line breaks should be written using writeLineDelimiter()
- * @param lineOffset offset of the line. 0 based from the start of the
- * widget document. Any text occurring before the start offset or after the
- * end offset specified during object creation is ignored.
- * @exception SWTException <ul>
- * <li>ERROR_IO when the writer is closed.</li>
- * </ul>
- */
- public void writeLine(String line, int lineOffset) {
- int lineLength = line.length();
- int lineIndex;
- int copyEnd;
- int writeOffset = startOffset - lineOffset;
-
- if (isClosed) {
- SWT.error(SWT.ERROR_IO);
- }
- if (writeOffset >= lineLength) {
- return; // whole line is outside write range
- }
- else
- if (writeOffset > 0) {
- lineIndex = writeOffset; // line starts before write start
- }
- else {
- lineIndex = 0;
- }
- copyEnd = Math.min(lineLength, endOffset - lineOffset);
- if (lineIndex < copyEnd) {
- write(line.substring(lineIndex, copyEnd));
- }
- }
- /**
- * Appends the specified line delmimiter to the data.
- * <p>
- *
- * @param lineDelimiter line delimiter to write
- * @exception SWTException <ul>
- * <li>ERROR_IO when the writer is closed.</li>
- * </ul>
- */
- public void writeLineDelimiter(String lineDelimiter) {
- if (isClosed) {
- SWT.error(SWT.ERROR_IO);
- }
- write(lineDelimiter);
- }
- }
- /**
- * LineCache provides an interface to calculate and invalidate
- * line based data.
- * Implementors need to return a line width in <code>getWidth</code>.
- */
- interface LineCache {
- /**
- * Calculates the lines in the specified range.
- * <p>
- *
- * @param startLine first line to calculate
- * @param lineCount number of lines to calculate
- */
- public void calculate(int startLine, int lineCount);
- /**
- * Returns a width that will be used by the <code>StyledText</code>
- * widget to size a horizontal scroll bar.
- * <p>
- *
- * @return the line width
- */
- public int getWidth();
- /**
- * Resets the lines in the specified range.
- * This method is called in <code>StyledText.redraw()</code>
- * and allows implementors to call redraw themselves during reset.
- * <p>
- *
- * @param startLine the first line to reset
- * @param lineCount the number of lines to reset
- * @param calculateMaxWidth true=implementors should retain a
- * valid width even if it is affected by the reset operation.
- * false=the width may be set to 0
- */
- public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth);
- /**
- * Resets the lines in the specified range.
- * <p>
- *
- * @param startLine the first line to reset
- * @param lineCount the number of lines to reset
- * @param calculateMaxWidth true=implementors should retain a
- * valid width even if it is affected by the reset operation.
- * false=the width may be set to 0
- */
- public void reset(int startLine, int lineCount, boolean calculateMaxWidth);
- /**
- * Called when a text change occurred.
- * <p>
- *
- * @param startOffset the start offset of the text change
- * @param newLineCount the number of inserted lines
- * @param replaceLineCount the number of deleted lines
- * @param newCharCount the number of new characters
- * @param replaceCharCount the number of deleted characters
- */
- public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount);
- }
- /**
- * Keeps track of line widths and the longest line in the
- * StyledText document.
- * Line widths are calculated when requested by a call to
- * <code>calculate</code> and cached until reset by a call
- * to <code>redrawReset</code> or <code>reset</code>.
- */
- class ContentWidthCache implements LineCache {
- StyledText parent; // parent widget, used to create a GC for line measuring
- int[] lineWidth; // width in pixel of each line in the document, -1 for unknown width
- int lineCount; // number of lines in lineWidth array
- int maxWidth; // maximum line width of all measured lines
- int maxWidthLineIndex; // index of the widest line
-
- /**
- * Creates a new <code>ContentWidthCache</code> and allocates space
- * for the given number of lines.
- * <p>
- *
- * @param parent the StyledText widget used to create a GC for
- * line measuring
- * @param lineCount initial number of lines to allocate space for
- */
- public ContentWidthCache(StyledText parent, int lineCount) {
- this.lineCount = lineCount;
- this.parent = parent;
- lineWidth = new int[lineCount];
- reset(0, lineCount, false);
- }
- /**
- * Calculates the width of each line in the given range if it has
- * not been calculated yet.
- * If any line in the given range is wider than the currently widest
- * line, the maximum line width is updated,
- * <p>
- *
- * @param startLine first line to calculate the line width of
- * @param lineCount number of lines to calculate the line width for
- */
- public void calculate(int startLine, int lineCount) {
- GC gc = null;
- int caretWidth = 0;
- int stopLine = startLine + lineCount;
-
- for (int i = startLine; i < stopLine; i++) {
- if (lineWidth[i] == -1) {
- String line = content.getLine(i);
- int lineOffset = content.getOffsetAtLine(i);
-
- if (gc == null) {
- gc = parent.getGC();
- caretWidth = getCaretWidth();
- }
- lineWidth[i] = contentWidth(line, lineOffset, gc) + caretWidth;
- }
- if (lineWidth[i] > maxWidth) {
- maxWidth = lineWidth[i];
- maxWidthLineIndex = i;
- }
- }
- if (gc != null) {
- gc.dispose();
- }
- }
- /**
- * Calculates the width of the visible lines in the specified
- * range.
- * <p>
- *
- * @param startLine the first changed line
- * @param newLineCount the number of inserted lines
- */
- void calculateVisible(int startLine, int newLineCount) {
- int topIndex = parent.getTopIndex();
- int bottomLine = Math.min(getPartialBottomIndex(), startLine + newLineCount);
-
- startLine = Math.max(startLine, topIndex);
- calculate(startLine, bottomLine - startLine + 1);
- }
- /**
- * Measures the width of the given line.
- * <p>
- *
- * @param line the line to measure
- * @param lineOffset start offset of the line to measure, relative
- * to the start of the document
- * @param gc the GC to use for measuring the line
- * @param currentFont the font currently set in gc. Cached for better
- * performance. Null when running in a bidi locale.
- * @return the width of the given line
- */
- int contentWidth(String line, int lineOffset, GC gc) {
- int width;
-
- if (isBidi()) {
- StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
- width = bidi.getTextWidth();
- }
- else {
- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
- StyleRange[] styles = null;
- if (event != null) {
- styles = renderer.filterLineStyles(event.styles);
- }
- width = renderer.getTextWidth(line, lineOffset, 0, line.length(), styles, 0, gc);
- }
- return width + leftMargin;
- }
- /**
- * Grows the <code>lineWidth</code> array to accomodate new line width
- * information.
- * <p>
- *
- * @param numLines the number of elements to increase the array by
- */
- void expandLines(int numLines) {
- int size = lineWidth.length;
- if (size - lineCount >= numLines) {
- return;
- }
- int[] newLines = new int[Math.max(size * 2, size + numLines)];
- System.arraycopy(lineWidth, 0, newLines, 0, size);
- lineWidth = newLines;
- reset(size, lineWidth.length - size, false);
- }
- /**
- * Returns the width of the longest measured line.
- * <p>
- *
- * @return the width of the longest measured line.
- */
- public int getWidth() {
- return maxWidth;
- }
- /**
- * Updates the line width array to reflect inserted or deleted lines.
- * <p>
- *
- * @param start the starting line of the change that took place
- * @param delta the number of lines in the change, > 0 indicates lines inserted,
- * < 0 indicates lines deleted
- */
- void linesChanged(int startLine, int delta) {
- boolean inserting = delta > 0;
-
- if (delta == 0) {
- return;
- }
- if (inserting) {
- // shift the lines down to make room for new lines
- expandLines(delta);
- for (int i = lineCount - 1; i >= startLine; i--) {
- lineWidth[i + delta] = lineWidth[i];
- }
- // reset the new lines
- for (int i = startLine + 1; i <= startLine + delta && i < lineWidth.length; i++) {
- lineWidth[i] = -1;
- }
- // have new lines been inserted above the longest line?
- if (maxWidthLineIndex >= startLine) {
- maxWidthLineIndex += delta;
- }
- }
- else {
- // shift up the lines
- for (int i = startLine - delta; i < lineCount; i++) {
- lineWidth[i+delta] = lineWidth[i];
- }
- // has the longest line been removed?
- if (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine - delta) {
- maxWidth = 0;
- maxWidthLineIndex = -1;
- }
- else
- if (maxWidthLineIndex >= startLine - delta) {
- maxWidthLineIndex += delta;
- }
- }
- lineCount += delta;
- }
- /**
- * Resets the line width of the lines in the specified range.
- * <p>
- *
- * @param startLine the first line to reset
- * @param lineCount the number of lines to reset
- * @param calculateMaxWidth true=if the widest line is being
- * reset the maximum width of all remaining cached lines is
- * calculated. false=the maximum width is set to 0 if the
- * widest line is being reset.
- */
- public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
- reset(startLine, lineCount, calculateMaxWidth);
- }
- /**
- * Resets the line width of the lines in the specified range.
- * <p>
- *
- * @param startLine the first line to reset
- * @param lineCount the number of lines to reset
- * @param calculateMaxWidth true=if the widest line is being
- * reset the maximum width of all remaining cached lines is
- * calculated. false=the maximum width is set to 0 if the
- * widest line is being reset.
- */
- public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
- int endLine = startLine + lineCount;
-
- if (startLine < 0 || endLine > lineWidth.length) {
- return;
- }
- for (int i = startLine; i < endLine; i++) {
- lineWidth[i] = -1;
- }
- // if the longest line is one of the reset lines, the maximum line
- // width is no longer valid
- if (maxWidthLineIndex >= startLine && maxWidthLineIndex < endLine) {
- maxWidth = 0;
- maxWidthLineIndex = -1;
- if (calculateMaxWidth) {
- for (int i = 0; i < lineCount; i++) {
- if (lineWidth[i] > maxWidth) {
- maxWidth = lineWidth[i];
- maxWidthLineIndex = i;
- }
- }
- }
- }
- }
- /**
- * Updates the line width array to reflect a text change.
- * Lines affected by the text change will be reset.
- * <p>
- *
- * @param startOffset the start offset of the text change
- * @param newLineCount the number of inserted lines
- * @param replaceLineCount the number of deleted lines
- * @param newCharCount the number of new characters
- * @param replaceCharCount the number of deleted characters
- */
- public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
- int startLine = parent.getLineAtOffset(startOffset);
- boolean removedMaxLine = (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine + replaceLineCount);
- // entire text deleted?
- if (startLine == 0 && replaceLineCount == lineCount) {
- lineCount = newLineCount;
- lineWidth = new int[lineCount];
- reset(0, lineCount, false);
- maxWidth = 0;
- }
- else {
- linesChanged(startLine, -replaceLineCount);
- linesChanged(startLine, newLineCount);
- lineWidth[startLine] = -1;
- }
- // only calculate the visible lines. otherwise measurements of changed lines
- // outside the visible area may subsequently change again without the
- // lines ever being visible.
- calculateVisible(startLine, newLineCount);
- // maxWidthLineIndex will be -1 (i.e., unknown line width) if the widget has
- // not been visible yet and the changed lines have therefore not been
- // calculated above.
- if (removedMaxLine ||
- (maxWidthLineIndex != -1 && lineWidth[maxWidthLineIndex] < maxWidth)) {
- // longest line has been removed or changed and is now shorter.
- // need to recalculate maximum content width for all lines
- maxWidth = 0;
- for (int i = 0; i < lineCount; i++) {
- if (lineWidth[i] > maxWidth) {
- maxWidth = lineWidth[i];
- maxWidthLineIndex = i;
- }
- }
- }
- }
- }
- /**
- * Updates the line wrapping of the content.
- * The line wrapping must always be in a consistent state.
- * Therefore, when <code>reset</code> or <code>redrawReset</code>
- * is called, the line wrapping is recalculated immediately
- * instead of in <code>calculate</code>.
- */
- class WordWrapCache implements LineCache {
- StyledText parent;
- WrappedContent visualContent;
-
- /**
- * Creates a new <code>WordWrapCache</code> and calculates an initial
- * line wrapping.
- * <p>
- *
- * @param parent the StyledText widget to wrap content in.
- * @param content the content provider that does the actual line wrapping.
- */
- public WordWrapCache(StyledText parent, WrappedContent content) {
- this.parent = parent;
- visualContent = content;
- visualContent.wrapLines();
- }
- /**
- * Do nothing. Lines are wrapped immediately after reset.
- * <p>
- *
- * @param startLine first line to calculate
- * @param lineCount number of lines to calculate
- */
- public void calculate(int startLine, int lineCount) {
- }
- /**
- * Returns the client area width. Lines are wrapped so there
- * is no horizontal scroll bar.
- * <p>
- *
- * @return the line width
- */
- public int getWidth() {
- return parent.getClientArea().width;
- }
- /**
- * Wraps the lines in the specified range.
- * This method is called in <code>StyledText.redraw()</code>.
- * A redraw is therefore not necessary.
- * <p>
- *
- * @param startLine the first line to reset
- * @param lineCount the number of lines to reset
- * @param calculateMaxWidth true=implementors should retain a
- * valid width even if it is affected by the reset operation.
- * false=the width may be set to 0
- */
- public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
- if (lineCount == visualContent.getLineCount()) {
- // do a full rewrap if all lines are reset
- visualContent.wrapLines();
- }
- else {
- visualContent.reset(startLine, lineCount);
- }
- }
- /**
- * Rewraps the lines in the specified range and redraws
- * the widget if the line wrapping has changed.
- * <p>
- *
- * @param startLine the first line to reset
- * @param lineCount the number of lines to reset
- * @param calculateMaxWidth true=implementors should retain a
- * valid width even if it is affected by the reset operation.
- * false=the width may be set to 0
- */
- public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
- int itemCount = getPartialBottomIndex() - topIndex + 1;
- int[] oldLineOffsets = new int[itemCount];
-
- for (int i = 0; i < itemCount; i++) {
- oldLineOffsets[i] = visualContent.getOffsetAtLine(i + topIndex);
- }
- redrawReset(startLine, lineCount, calculateMaxWidth);
- // check for cases which will require a full redraw
- if (getPartialBottomIndex() - topIndex + 1 != itemCount) {
- // number of visible lines has changed
- parent.internalRedraw();
- }
- else {
- for (int i = 0; i < itemCount; i++) {
- if (visualContent.getOffsetAtLine(i + topIndex) != oldLineOffsets[i]) {
- // wrapping of one of the visible lines has changed
- parent.internalRedraw();
- break;
- }
- }
- }
- }
- /**
- * Passes the text change notification to the line wrap content.
- * <p>
- *
- * @param startOffset the start offset of the text change
- * @param newLineCount the number of inserted lines
- * @param replaceLineCount the number of deleted lines
- * @param newCharCount the number of new characters
- * @param replaceCharCount the number of deleted characters
- */
- public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
- int startLine = visualContent.getLineAtOffset(startOffset);
-
- visualContent.textChanged(startOffset, newLineCount, replaceLineCount, newCharCount, replaceCharCount);
- if (startLine <= getPartialBottomIndex()) {
- // only redraw if the text change is inside or above the
- // visible lines. if it is below the visible lines it will
- // not affect the word wrapping. fixes bug 14047.
- parent.internalRedraw();
- }
- }
- }
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#FULL_SELECTION
- * @see SWT#MULTI
- * @see SWT#READ_ONLY
- * @see SWT#SINGLE
- * @see SWT#WRAP
- * @see #getStyle
- */
-public StyledText(Composite parent, int style) {
- super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND));
- // set the bg/fg in the OS to ensure that these are the same as StyledText, necessary
- // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
- super.setForeground(getForeground());
- super.setBackground(getBackground());
- Display display = getDisplay();
- isBidi = StyledTextBidi.isBidiPlatform();
- if ((style & SWT.READ_ONLY) != 0) {
- setEditable(false);
- }
- if ((style & SWT.BORDER) == 0 || (style & SWT.SINGLE) == 0) {
- leftMargin = topMargin = rightMargin = bottomMargin = 0;
- }
- clipboard = new Clipboard(display);
- installDefaultContent();
- initializeRenderer();
- if ((style & SWT.WRAP) != 0) {
- setWordWrap(true);
- }
- else {
- lineCache = new ContentWidthCache(this, content.getLineCount());
- }
- if (isBidi() == false) {
- Caret caret = new Caret(this, SWT.NULL);
- caret.setSize(1, caret.getSize().y);
- }
- else {
- createCaretBitmaps();
- new Caret(this, SWT.NULL);
- setBidiCaretDirection();
- Runnable runnable = new Runnable() {
- public void run() {
- // setBidiCaretLocation calculates caret location like during
- // cursor movement and takes keyboard language into account.
- // Fixes 1GKPYMK
- setBidiCaretLocation(null);
- }
- };
- StyledTextBidi.addLanguageListener(this, runnable);
- }
-
- String platform= SWT.getPlatform();
- isCarbon = "carbon".equals(platform);
-
- // set the caret width, the height of the caret will default to the line height
- calculateScrollBars();
- createKeyBindings();
- ibeamCursor = new Cursor(display, SWT.CURSOR_IBEAM);
- setCursor(ibeamCursor);
- installListeners();
- installDefaultLineStyler();
-}
-/**
- * Adds an extended modify listener. An ExtendedModify event is sent by the
- * widget when the widget text has changed.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
- checkWidget();
- if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
- addListener(ExtendedModify, typedListener);
-}
-/**
- * Maps a key to an action.
- * One action can be associated with N keys. However, each key can only
- * have one action (key:action is N:1 relation).
- * <p>
- *
- * @param key a key code defined in SWT.java or a character.
- * Optionally ORd with a state mask. Preferred state masks are one or more of
- * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
- * differences. However, there may be cases where using the specific state masks
- * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
- * @param action one of the predefined actions defined in ST.java.
- * Use SWT.NULL to remove a key binding.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setKeyBinding(int key, int action) {
- checkWidget();
-
- int keyValue = key & SWT.KEY_MASK;
- int modifierValue = key & SWT.MODIFIER_MASK;
- char keyChar = (char)keyValue;
-
- if (Character.isLetter(keyChar)) {
- // make the keybinding case insensitive by adding it
- // in its upper and lower case form
- char ch = Character.toUpperCase(keyChar);
- int newKey = ch | modifierValue;
- if (action == SWT.NULL) {
- keyActionMap.remove(new Integer(newKey));
- }
- else {
- keyActionMap.put(new Integer(newKey), new Integer(action));
- }
- ch = Character.toLowerCase(keyChar);
- newKey = ch | modifierValue;
- if (action == SWT.NULL) {
- keyActionMap.remove(new Integer(newKey));
- }
- else {
- keyActionMap.put(new Integer(newKey), new Integer(action));
- }
- } else {
- if (action == SWT.NULL) {
- keyActionMap.remove(new Integer(key));
- }
- else {
- keyActionMap.put(new Integer(key), new Integer(action));
- }
- }
-
-}
-/**
- * Adds a bidirectional segment listener. A BidiSegmentEvent is sent
- * whenever a line of text is measured or rendered. The user can
- * specify text ranges in the line that should be treated as if they
- * 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
- * should generally remain in logical order to each other, that is, the
- * way they are stored.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- * @see BidiSegmentEvent
- * @since 2.0
- */
-public void addBidiSegmentListener(BidiSegmentListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- StyledTextListener typedListener = new StyledTextListener(listener);
- addListener(LineGetSegments, typedListener);
-}
-/**
- * Adds a line background listener. A LineGetBackground event is sent by the
- * widget to determine the background color for a line.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addLineBackgroundListener(LineBackgroundListener listener) {
- checkWidget();
- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- if (userLineBackground == false) {
- removeLineBackgroundListener(defaultLineStyler);
- defaultLineStyler.setLineBackground(0, logicalContent.getLineCount(), null);
- userLineBackground = true;
- }
- StyledTextListener typedListener = new StyledTextListener(listener);
- addListener(LineGetBackground, typedListener);
-}
-/**
- * Adds a line style listener. A LineGetStyle event is sent by the widget to
- * determine the styles for a line.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addLineStyleListener(LineStyleListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- if (userLineStyle == false) {
- removeLineStyleListener(defaultLineStyler);
- defaultLineStyler.setStyleRange(null);
- userLineStyle = true;
- }
- StyledTextListener typedListener = new StyledTextListener(listener);
- addListener(LineGetStyle, typedListener);
-}
-/**
- * Adds a modify listener. A Modify event is sent by the widget when the widget text
- * has changed.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addModifyListener(ModifyListener modifyListener) {
- checkWidget();
- if (modifyListener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- TypedListener typedListener = new TypedListener(modifyListener);
- addListener(SWT.Modify, typedListener);
-}
-/**
- * Adds a selection listener. A Selection event is sent by the widget when the
- * selection has changed.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- TypedListener typedListener = new TypedListener(listener);
- addListener(SWT.Selection, typedListener);
-}
-/**
- * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
- * is pressed. The widget ignores the key press if the listener sets the doit field
- * of the event to false.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addVerifyKeyListener(VerifyKeyListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- StyledTextListener typedListener = new StyledTextListener(listener);
- addListener(VerifyKey, typedListener);
-}
-/**
- * Adds a verify listener. A Verify event is sent by the widget when the widget text
- * is about to change. The listener can set the event text and the doit field to
- * change the text that is set in the widget or to force the widget to ignore the
- * text change.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addVerifyListener(VerifyListener verifyListener) {
- checkWidget();
- if (verifyListener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- TypedListener typedListener = new TypedListener(verifyListener);
- addListener(SWT.Verify, typedListener);
-}
-/**
- * Appends a string to the text at the end of the widget.
- * <p>
- *
- * @param string the string to be appended
- * @see #replaceTextRange(int,int,String)
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void append(String string) {
- checkWidget();
- if (string == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- int lastChar = Math.max(getCharCount(), 0);
- replaceTextRange(lastChar, 0, string);
-}
-/**
- * Calculates the width of the widest visible line.
- */
-void calculateContentWidth() {
- if (lineHeight != 0) {
- lineCache = getLineCache(content);
- lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
- }
-}
-/**
- * Calculates the scroll bars
- */
-void calculateScrollBars() {
- ScrollBar horizontalBar = getHorizontalBar();
- ScrollBar verticalBar = getVerticalBar();
-
- setScrollBars();
- if (verticalBar != null) {
- verticalBar.setIncrement(getVerticalIncrement());
- }
- if (horizontalBar != null) {
- horizontalBar.setIncrement(getHorizontalIncrement());
- }
-}
-/**
- * Calculates the top index based on the current vertical scroll offset.
- * The top index is the index of the topmost fully visible line or the
- * topmost partially visible line if no line is fully visible.
- * The top index starts at 0.
- */
-void calculateTopIndex() {
- int oldTopIndex = topIndex;
- int verticalIncrement = getVerticalIncrement();
- int clientAreaHeight = getClientArea().height;
-
- if (verticalIncrement == 0) {
- return;
- }
- topIndex = Compatibility.ceil(verticalScrollOffset, verticalIncrement);
- // Set top index to partially visible top line if no line is fully
- // visible but at least some of the widget client area is visible.
- // Fixes bug 15088.
- if (topIndex > 0) {
- if (clientAreaHeight > 0) {
- int bottomPixel = verticalScrollOffset + clientAreaHeight;
- int fullLineTopPixel = topIndex * verticalIncrement;
- int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
- // set top index to partially visible line if no line fully fits in
- // client area or if space is available but not used (the latter should
- // never happen because we use claimBottomFreeSpace)
- if (fullLineVisibleHeight < verticalIncrement) {
- topIndex--;
- }
- }
- else
- if (topIndex >= content.getLineCount()) {
- topIndex = content.getLineCount() - 1;
- }
- }
- if (topIndex != oldTopIndex) {
- topOffset = content.getOffsetAtLine(topIndex);
- lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
- setHorizontalScrollBar();
- }
-}
-/**
- * Hides the scroll bars if widget is created in single line mode.
- */
-static int checkStyle(int style) {
- if ((style & SWT.SINGLE) != 0) {
- style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
- }
- return style;
-}
-/**
- * Scrolls down the text to use new space made available by a resize or by
- * deleted lines.
- */
-void claimBottomFreeSpace() {
- int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - getClientArea().height);
-
- if (newVerticalOffset < verticalScrollOffset) {
- // Scroll up so that empty lines below last text line are used.
- // Fixes 1GEYJM0
- setVerticalScrollOffset(newVerticalOffset, true);
- }
-}
-/**
- * Scrolls text to the right to use new space made available by a resize.
- */
-void claimRightFreeSpace() {
- int newHorizontalOffset = Math.max(0, lineCache.getWidth() - (getClientArea().width - leftMargin - rightMargin));
-
- if (newHorizontalOffset < horizontalScrollOffset) {
- // item is no longer drawn past the right border of the client area
- // align the right end of the item with the right border of the
- // client area (window is scrolled right).
- scrollHorizontalBar(newHorizontalOffset - horizontalScrollOffset);
- }
-}
-/**
- * Clears the widget margin.
- *
- * @param gc GC to render on
- * @param background background color to use for clearing the margin
- * @param clientArea widget client area dimensions
- * @param renderHeight height in pixel of the rendered lines
- */
-void clearMargin(GC gc, Color background, Rectangle clientArea, int renderHeight) {
- if (renderHeight + topMargin <= 0) {
- return;
- }
- // clear the margin background
- gc.setBackground(background);
- gc.fillRectangle(0, 0, clientArea.width, topMargin);
- gc.fillRectangle(0, 0, leftMargin, renderHeight + topMargin);
- gc.fillRectangle(
- 0, clientArea.height - bottomMargin,
- clientArea.width, bottomMargin);
- gc.fillRectangle(
- clientArea.width - rightMargin, 0,
- rightMargin, renderHeight + topMargin);
-}
-/**
- * Removes the widget selection.
- * <p>
- *
- * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
- */
-void clearSelection(boolean sendEvent) {
- int selectionStart = selection.x;
- int selectionEnd = selection.y;
- int length = content.getCharCount();
-
- resetSelection();
- // redraw old selection, if any
- if (selectionEnd - selectionStart > 0) {
- // called internally to remove selection after text is removed
- // therefore make sure redraw range is valid.
- int redrawStart = Math.min(selectionStart, length);
- int redrawEnd = Math.min(selectionEnd, length);
- if (redrawEnd - redrawStart > 0) {
- internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
- }
- if (sendEvent == true) {
- sendSelectionEvent();
- }
- }
-}
-/**
- * Computes the preferred size.
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public Point computeSize (int wHint, int hHint, boolean changed) {
- checkWidget();
- int count, width, height;
- boolean singleLine = (getStyle() & SWT.SINGLE) != 0;
-
- if (singleLine) {
- count = 1;
- } else {
- count = content.getLineCount();
- }
- if (wHint != SWT.DEFAULT) {
- width = wHint;
- }
- else {
- width = DEFAULT_WIDTH;
- }
-
- if (wordWrap) {
- if (((WrappedContent) content).getVisualLineCount() != 0) {
- // lines have already been wrapped to a specific width.
- // use existing line count. fixes bug 9191
- if (wHint == SWT.DEFAULT) {
- width = lineCache.getWidth();
- } else {
- ((WrappedContent) content).wrapLines(width);
- // caret may be on a different line after a rewrap
- setCaretLocation();
- }
- if (singleLine == false) {
- count = content.getLineCount();
- }
- }
- else {
- if (singleLine == false) {
- ((WrappedContent) content).wrapLines(width);
- // caret may be on a different line after a rewrap
- setCaretLocation();
- count = content.getLineCount();
- }
- }
- }
- else if (wHint == SWT.DEFAULT) {
- // Only calculate what can actually be displayed.
- // Do this because measuring each text line is a
- // time-consuming process.
- int visibleCount = Math.min (count, getDisplay().getBounds().height / lineHeight);
- lineCache.calculate(0, visibleCount);
- width = lineCache.getWidth() + leftMargin + rightMargin;
- }
- if (hHint != SWT.DEFAULT) {
- height = hHint;
- }
- else {
- height = count * lineHeight + topMargin + bottomMargin;
- }
- // Use default values if no text is defined.
- if (width == 0) {
- width = DEFAULT_WIDTH;
- }
- if (height == 0) {
- if (singleLine) {
- height = lineHeight;
- }
- else {
- height = DEFAULT_HEIGHT;
- }
- }
- Rectangle rect = computeTrim(0, 0, width, height);
- return new Point (rect.width, rect.height);
-}
-/**
- * Copies the selected text to the clipboard. The text will be put in the
- * clipboard in plain text format and RTF format.
- * <p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void copy() {
- checkWidget();
- int length = selection.y - selection.x;
- if (length > 0) {
- try {
- setClipboardContent(selection.x, length);
- }
- catch (SWTError error) {
- // Copy to clipboard failed. This happens when another application
- // is accessing the clipboard while we copy. Ignore the error.
- // Fixes 1GDQAVN
- // Rethrow all other errors. Fixes bug 17578.
- if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
- throw error;
- }
- }
- }
-}
-/**
- * Returns a string that uses only the line delimiter specified by the
- * StyledTextContent implementation.
- * Returns only the first line if the widget has the SWT.SINGLE style.
- * <p>
- *
- * @param text the text that may have line delimiters that don't
- * match the model line delimiter. Possible line delimiters
- * are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
- * @return the converted text that only uses the line delimiter
- * specified by the model. Returns only the first line if the widget
- * has the SWT.SINGLE style.
- */
-String getModelDelimitedText(String text) {
- StringBuffer convertedText;
- String delimiter = getLineDelimiter();
- int length = text.length();
- int crIndex = 0;
- int lfIndex = 0;
- int i = 0;
-
- if (length == 0) {
- return text;
- }
- convertedText = new StringBuffer(length);
- while (i < length) {
- if (crIndex != -1) {
- crIndex = text.indexOf(SWT.CR, i);
- }
- if (lfIndex != -1) {
- lfIndex = text.indexOf(SWT.LF, i);
- }
- if (lfIndex == -1 && crIndex == -1) { // no more line breaks?
- break;
- }
- else // CR occurs before LF or no LF present?
- if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
- convertedText.append(text.substring(i, crIndex));
- if (lfIndex == crIndex + 1) { // CR/LF combination?
- i = lfIndex + 1;
- }
- else {
- i = crIndex + 1;
- }
- }
- else { // LF occurs before CR!
- convertedText.append(text.substring(i, lfIndex));
- i = lfIndex + 1;
- }
- if (isSingleLine()) {
- break;
- }
- convertedText.append(delimiter);
- }
- // copy remaining text if any and if not in single line mode or no
- // text copied thus far (because there only is one line)
- if (i < length && (isSingleLine() == false || convertedText.length() == 0)) {
- convertedText.append(text.substring(i));
- }
- return convertedText.toString();
-}
-/**
- * Creates default key bindings.
- */
-void createKeyBindings() {
- // Navigation
- setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
- setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
- setKeyBinding(SWT.HOME, ST.LINE_START);
- setKeyBinding(SWT.END, ST.LINE_END);
- setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS);
- setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT);
- setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
- setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
- setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS);
- setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT);
- setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
- setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
- setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
- setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
- // Selection
- setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
- setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
- setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
- setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
- setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
- setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
- setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
- setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
- setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
- setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
- setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
- setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
- setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
- setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
-
- // Modification
- // Cut, Copy, Paste
- setKeyBinding('X' | SWT.MOD1, ST.CUT);
- setKeyBinding('C' | SWT.MOD1, ST.COPY);
- setKeyBinding('V' | SWT.MOD1, ST.PASTE);
- // Cut, Copy, Paste Wordstar style
- setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
- setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
- setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
- setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
-
- setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
- setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
- setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
- setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
-
- // Miscellaneous
- setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
-}
-/**
- * Create the bitmaps to use for the caret in bidi mode. This
- * method only needs to be called upon widget creation and when the
- * font changes (the caret bitmap height needs to match font height).
- */
-void createCaretBitmaps() {
- int caretWidth = BIDI_CARET_WIDTH;
-
- Display display = getDisplay();
- if (caretPalette == null) {
- caretPalette = new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
- }
- if (leftCaretBitmap != null) {
- leftCaretBitmap.dispose();
- }
- ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette);
- leftCaretBitmap = new Image(display, imageData);
- GC gc = new GC (leftCaretBitmap);
- gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
- gc.drawLine(0,0,0,lineHeight);
- gc.drawLine(0,0,caretWidth-1,0);
- gc.drawLine(0,1,1,1);
- gc.dispose();
-
- if (rightCaretBitmap != null) {
- rightCaretBitmap.dispose();
- }
- rightCaretBitmap = new Image(display, imageData);
- gc = new GC (rightCaretBitmap);
- gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
- gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
- gc.drawLine(0,0,caretWidth-1,0);
- gc.drawLine(caretWidth-1,1,1,1);
- gc.dispose();
-}
-/**
- * Moves the selected text to the clipboard. The text will be put in the
- * clipboard in plain text format and RTF format.
- * <p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void cut(){
- checkWidget();
- int length = selection.y - selection.x;
-
- if (length > 0) {
- try {
- setClipboardContent(selection.x, length);
- }
- catch (SWTError error) {
- // Copy to clipboard failed. This happens when another application
- // is accessing the clipboard while we copy. Ignore the error.
- // Fixes 1GDQAVN
- // Rethrow all other errors. Fixes bug 17578.
- if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
- throw error;
- }
- // Abort cut operation if copy to clipboard fails.
- // Fixes bug 21030.
- return;
- }
- doDelete();
- }
-}
-/**
- * A mouse move event has occurred. See if we should start autoscrolling. If
- * the move position is outside of the client area, initiate autoscrolling.
- * Otherwise, we've moved back into the widget so end autoscrolling.
- */
-void doAutoScroll(Event event) {
- Rectangle area = getClientArea();
-
- if (event.y > area.height) {
- doAutoScroll(SWT.DOWN);
- }
- else
- if (event.y < 0) {
- doAutoScroll(SWT.UP);
- }
- else
- if (event.x < leftMargin && wordWrap == false) {
- doAutoScroll(SWT.LEFT);
- }
- else
- if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) {
- doAutoScroll(SWT.RIGHT);
- }
- else {
- endAutoScroll();
- }
-}
-/**
- * Initiates autoscrolling.
- * <p>
- *
- * @param direction SWT.UP, SWT.DOWN, SWT.RIGHT, SWT.LEFT
- */
-void doAutoScroll(int direction) {
- Runnable timer = null;
- final int TIMER_INTERVAL = 5;
-
- // If we're already autoscrolling in the given direction do nothing
- if (autoScrollDirection == direction) {
- return;
- }
-
- final Display display = getDisplay();
- // Set a timer that will simulate the user pressing and holding
- // down a cursor key (i.e., arrowUp, arrowDown).
- if (direction == SWT.UP) {
- timer = new Runnable() {
- public void run() {
- if (autoScrollDirection == SWT.UP) {
- doSelectionLineUp();
- display.timerExec(TIMER_INTERVAL, this);
- }
- }
- };
- } else if (direction == SWT.DOWN) {
- timer = new Runnable() {
- public void run() {
- if (autoScrollDirection == SWT.DOWN) {
- doSelectionLineDown();
- display.timerExec(TIMER_INTERVAL, this);
- }
- }
- };
- } else if (direction == SWT.RIGHT) {
- timer = new Runnable() {
- public void run() {
- if (autoScrollDirection == SWT.RIGHT) {
- doColumnRight();
- setMouseWordSelectionAnchor();
- doSelection(SWT.RIGHT);
- display.timerExec(TIMER_INTERVAL, this);
- }
- }
- };
- } else if (direction == SWT.LEFT) {
- timer = new Runnable() {
- public void run() {
- if (autoScrollDirection == SWT.LEFT) {
- doColumnLeft();
- setMouseWordSelectionAnchor();
- doSelection(SWT.LEFT);
- display.timerExec(TIMER_INTERVAL, this);
- }
- }
- };
- }
- if (timer != null) {
- autoScrollDirection = direction;
- display.timerExec(TIMER_INTERVAL, timer);
- }
-}
-/**
- * Deletes the previous character. Delete the selected text if any.
- * Move the caret in front of the deleted text.
- */
-void doBackspace() {
- Event event = new Event();
- event.text = "";
- if (selection.x != selection.y) {
- event.start = selection.x;
- event.end = selection.y;
- sendKeyEvent(event);
- }
- else
- if (caretOffset > 0) {
- int line = content.getLineAtOffset(caretOffset);
- int lineOffset = content.getOffsetAtLine(line);
-
- if (caretOffset == lineOffset) {
- lineOffset = content.getOffsetAtLine(line - 1);
- event.start = lineOffset + content.getLine(line - 1).length();
- event.end = caretOffset;
- }
- else {
- event.start = caretOffset - 1;
- event.end = caretOffset;
- }
- sendKeyEvent(event);
- }
-}
-/**
- * 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
- * left (visually left because it's now in a L2R segment).
- */
-void doColumnLeft() {
- int line = content.getLineAtOffset(caretOffset);
- int lineOffset = content.getOffsetAtLine(line);
- int offsetInLine = caretOffset - lineOffset;
-
- if (isBidi()) {
- String lineText = content.getLine(line);
- int lineLength = lineText.length();
- GC gc = getGC();
- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
-
- if (horizontalScrollOffset > 0 || offsetInLine > 0) {
- if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) {
- // advance caret logically if in R2L segment (move visually left)
- caretOffset++;
- doSelection(SWT.RIGHT);
- if (caretOffset - lineOffset == lineLength) {
- // if the line end is reached in a R2L segment, make the
- // caret position (visual left border) visible before
- // jumping to segment start
- showCaret();
- }
- // end of R2L segment reached (visual left side)?
- if (bidi.isRightToLeft(caretOffset - lineOffset) == false) {
- if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
- // make beginning of R2L segment visible before going
- // left, to L2R segment important if R2L segment ends
- // at visual left in order to scroll all the way to the
- // left. Fixes 1GKM3XS
- showCaret();
- }
- // go to beginning of R2L segment (visually end of next L2R
- // segment)/beginning of line
- caretOffset--;
- while (caretOffset - lineOffset > 0 &&
- bidi.isRightToLeft(caretOffset - lineOffset)) {
- caretOffset--;
- }
- }
- }
- else
- if (offsetInLine == lineLength &&
- bidi.getTextPosition(lineLength) != XINSET) {
- // at logical line end in R2L segment but there's more text (a
- // L2R segment) go to end of R2L segment (visually left of next
- // L2R segment)/end of line
- caretOffset--;
- while (caretOffset - lineOffset > 0 &&
- bidi.isRightToLeft(caretOffset - lineOffset)) {
- caretOffset--;
- }
- }
- else
- if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
- // decrease caret logically if in L2R segment (move visually left)
- caretOffset--;
- doSelection(SWT.LEFT);
- // end of L2R segment reached (visual left side of preceeding R2L
- // segment)?
- if (caretOffset - lineOffset > 0 &&
- bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
- // go to beginning of R2L segment (visually start of next L2R
- // segment)/beginning of line
- caretOffset--;
- while (caretOffset - lineOffset > 0 &&
- bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
- caretOffset--;
- }
- }
- }
- // if new caret position is to the left of the client area
- if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
- // scroll to the caret position
- showCaret();
- }
- else {
- // otherwise just update caret position without scrolling it into view
- setBidiCaretLocation(null);
- setBidiKeyboardLanguage();
- }
- // Beginning of line reached (auto scroll finished) but not scrolled
- // completely to the left? Fixes 1GKM193
- if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 &&
- horizontalScrollOffset <= XINSET) {
- scrollHorizontalBar(-horizontalScrollOffset);
- }
- }
- gc.dispose();
- }
- else
- if (offsetInLine > 0) {
- caretOffset--;
- showCaret();
- }
-}
-/**
- * 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
- * right (visually right because it's now in a L2R segment).
- */
-void doColumnRight() {
- int line = content.getLineAtOffset(caretOffset);
- int lineOffset = content.getOffsetAtLine(line);
- int offsetInLine = caretOffset - lineOffset;
- String lineText = content.getLine(line);
- int lineLength = lineText.length();
-
- if (isBidi()) {
- GC gc = getGC();
- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
- if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
- offsetInLine < lineLength) {
- if (bidi.isRightToLeft(offsetInLine) == false &&
- offsetInLine < lineLength) {
- // advance caret logically if in L2R segment (move visually right)
- caretOffset++;
- doSelection(SWT.RIGHT);
- // end of L2R segment reached (visual right side)?
- if (bidi.isRightToLeft(caretOffset - lineOffset)) {
- // go to end of R2L segment (visually left of next R2L segment)/
- // end of line
- caretOffset++;
- while (caretOffset < lineOffset + lineLength &&
- bidi.isRightToLeft(caretOffset - lineOffset)) {
- caretOffset++;
- }
- }
- }
- else
- if (offsetInLine > 0 &&
- (bidi.isRightToLeft(offsetInLine) ||
- bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
- offsetInLine < lineLength)) {
- // advance caret visually if in R2L segment or logically at line end
- // but right end of line is not fully visible yet
- caretOffset--;
- doSelection(SWT.LEFT);
- offsetInLine = caretOffset - lineOffset;
- // end of R2L segment reached (visual right side)?
- if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
- // go to end of R2L segment (visually left of next L2R segment)/
- // end of line
- caretOffset++;
- while (caretOffset < lineOffset + lineLength &&
- bidi.isRightToLeft(caretOffset - lineOffset)) {
- caretOffset++;
- }
- }
- }
- else
- if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) {
- // at logical line start in R2L segment but there's more text (a L2R
- // segment) go to end of R2L segment (visually left of next L2R
- // segment)/end of line
- caretOffset++;
- while (caretOffset < lineOffset + lineLength &&
- bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
- caretOffset++;
- }
- }
- offsetInLine = caretOffset - lineOffset;
- // if new caret position is to the right of the client area
- if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) {
- // scroll to the caret position
- showCaret();
- }
- else {
- // otherwise just update caret position without scrolling it into view
- setBidiCaretLocation(null);
- setBidiKeyboardLanguage();
- }
- if (offsetInLine > 0 && offsetInLine < lineLength - 1) {
- int clientAreaEnd = horizontalScrollOffset + getClientArea().width;
- boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine);
- int textWidth = bidi.getTextWidth() + leftMargin;
- // between L2R and R2L segment and second character of R2L segment is
- // left of right border and logical line end is left of right border
- // but visual line end is not left of right border
- if (directionChange &&
- bidi.isRightToLeft(offsetInLine + 1) &&
- bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd &&
- bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) {
- // make visual line end visible
- scrollHorizontalBar(textWidth - clientAreaEnd);
- }
- }
- }
- gc.dispose();
- }
- else
- if (offsetInLine < lineLength) {
- caretOffset++;
- showCaret();
- }
-}
-/**
- * Replaces the selection with the character or insert the character at the
- * current caret position if no selection exists.
- * If a carriage return was typed replace it with the line break character
- * used by the widget on this platform.
- * <p>
- *
- * @param key the character typed by the user
- */
-void doContent(char key) {
- Event event;
-
- if (textLimit > 0 &&
- content.getCharCount() - (selection.y - selection.x) >= textLimit) {
- return;
- }
- event = new Event();
- event.start = selection.x;
- event.end = selection.y;
- // replace a CR line break with the widget line break
- // CR does not make sense on Windows since most (all?) applications
- // don't recognize CR as a line break.
- if (key == SWT.CR || key == SWT.LF) {
- if (isSingleLine() == false) {
- event.text = getLineDelimiter();
- }
- }
- // no selection and overwrite mode is on and the typed key is not a
- // tab character (tabs are always inserted without overwriting)?
- else
- if (selection.x == selection.y && overwrite == true && key != TAB) {
- int lineIndex = content.getLineAtOffset(event.end);
- int lineOffset = content.getOffsetAtLine(lineIndex);
- String line = content.getLine(lineIndex);
- // replace character at caret offset if the caret is not at the
- // end of the line
- if (event.end < lineOffset + line.length()) {
- event.end++;
- }
- event.text = new String(new char[] {key});
- }
- else {
- event.text = new String(new char[] {key});
- }
- if (event.text != null) {
- sendKeyEvent(event);
- }
-}
-/**
- * Moves the caret after the last character of the widget content.
- */
-void doContentEnd() {
- // place caret at end of first line if receiver is in single
- // line mode. fixes 4820.
- if (isSingleLine()) {
- doLineEnd();
- }
- else {
- int length = content.getCharCount();
- if (caretOffset < length) {
- caretOffset = length;
- showCaret();
- }
- }
-}
-/**
- * Moves the caret in front of the first character of the widget content.
- */
-void doContentStart() {
- if (caretOffset > 0) {
- caretOffset = 0;
- showCaret();
- }
-}
-/**
- * Moves the caret to the start of the selection if a selection exists.
- * Otherwise, if no selection exists move the cursor according to the
- * cursor selection rules.
- * <p>
- *
- * @see #doSelectionCursorPrevious
- */
-void doCursorPrevious() {
- if (selection.y - selection.x > 0) {
- int caretLine;
-
- caretOffset = selection.x;
- caretLine = getCaretLine();
- showCaret(caretLine);
- }
- else {
- doSelectionCursorPrevious();
- }
-}
-/**
- * Moves the caret to the end of the selection if a selection exists.
- * Otherwise, if no selection exists move the cursor according to the
- * cursor selection rules.
- * <p>
- *
- * @see #doSelectionCursorNext
- */
-void doCursorNext() {
- if (selection.y - selection.x > 0) {
- int caretLine;
-
- caretOffset = selection.y;
- caretLine = getCaretLine();
- showCaret(caretLine);
- }
- else {
- doSelectionCursorNext();
- }
-}
-/**
- * Deletes the next character. Delete the selected text if any.
- */
-void doDelete() {
- Event event = new Event();
-
- event.text = "";
- if (selection.x != selection.y) {
- event.start = selection.x;
- event.end = selection.y;
- sendKeyEvent(event);
- }
- else
- if (caretOffset < content.getCharCount()) {
- int line = content.getLineAtOffset(caretOffset);
- int lineOffset = content.getOffsetAtLine(line);
- int lineLength = content.getLine(line).length();
-
- if (caretOffset == lineOffset + lineLength) {
- event.start = caretOffset;
- event.end = content.getOffsetAtLine(line + 1);
- }
- else {
- event.start = caretOffset;
- event.end = caretOffset + 1;
- }
- sendKeyEvent(event);
- }
-}
-/**
- * Deletes the next word.
- */
-void doDeleteWordNext() {
- if (selection.x != selection.y) {
- // if a selection exists, treat the as if
- // only the delete key was pressed
- doDelete();
- } else {
- Event event = new Event();
- event.text = "";
- event.start = caretOffset;
- event.end = getWordEnd(caretOffset);
- sendKeyEvent(event);
- }
-}
-/**
- * Deletes the previous word.
- */
-void doDeleteWordPrevious() {
- if (selection.x != selection.y) {
- // if a selection exists, treat as if
- // only the backspace key was pressed
- doBackspace();
- } else {
- Event event = new Event();
- event.text = "";
- event.start = getWordStart(caretOffset);
- event.end = caretOffset;
- sendKeyEvent(event);
- }
-}
-/**
- * Moves the caret one line down and to the same character offset relative
- * to the beginning of the line. Move the caret to the end of the new line
- * if the new line is shorter than the character offset.
- *
- * @return index of the new line relative to the first line in the document
- */
-int doLineDown() {
- if (isSingleLine()) {
- return 0;
- }
- // allow line down action only if receiver is not in single line mode.
- // fixes 4820.
- int caretLine = getCaretLine();
- if (caretLine < content.getLineCount() - 1) {
- caretLine++;
- if (isBidi()) {
- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
- }
- else {
- caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
- }
- }
- return caretLine;
-}
-/**
- * Moves the caret to the end of the line.
- */
-void doLineEnd() {
- int caretLine = getCaretLine();
- int lineOffset = content.getOffsetAtLine(caretLine);
- int lineLength = content.getLine(caretLine).length();
- int lineEndOffset = lineOffset + lineLength;
-
- if (caretOffset < lineEndOffset) {
- caretOffset = lineEndOffset;
- showCaret();
- }
-}
-/**
- * Moves the caret to the beginning of the line.
- */
-void doLineStart() {
- int caretLine = getCaretLine();
- int lineOffset = content.getOffsetAtLine(caretLine);
-
- if (caretOffset > lineOffset) {
- caretOffset = lineOffset;
- showCaret(caretLine);
- }
-}
-/**
- * Moves the caret one line up and to the same character offset relative
- * to the beginning of the line. Move the caret to the end of the new line
- * if the new line is shorter than the character offset.
- *
- * @return index of the new line relative to the first line in the document
- */
-int doLineUp() {
- int caretLine = getCaretLine();
-
- if (caretLine > 0) {
- caretLine--;
- if (isBidi()) {
- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
- }
- else {
- caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
- }
- }
- return caretLine;
-}
-/**
- * Moves the caret to the specified location.
- * <p>
- *
- * @param x x location of the new caret position
- * @param y y location of the new caret position
- * @param select the location change is a selection operation.
- * include the line delimiter in the selection
- */
-void doMouseLocationChange(int x, int y, boolean select) {
- int line = (y + verticalScrollOffset) / lineHeight;
- int lineCount = content.getLineCount();
- int newCaretOffset;
- int newCaretLine;
-
- if (line > lineCount - 1) {
- line = lineCount - 1;
- }
- // allow caret to be placed below first line only if receiver is
- // not in single line mode. fixes 4820.
- if (line < 0 || (isSingleLine() && line > 0)) {
- return;
- }
- if (isBidi()) {
- newCaretOffset = getBidiOffsetAtMouseLocation(x, line);
- }
- else {
- newCaretOffset = getOffsetAtMouseLocation(x, line);
- }
- if (mouseDoubleClick) {
- // double click word select the previous/next word. fixes bug 15610
- newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
- }
- newCaretLine = content.getLineAtOffset(newCaretOffset);
- // Is the mouse within the left client area border or on
- // a different line? If not the autoscroll selection
- // could be incorrectly reset. Fixes 1GKM3XS
- if (y >= 0 && y < getClientArea().height &&
- (x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) {
- if (newCaretOffset != caretOffset) {
- caretOffset = newCaretOffset;
- if (select) {
- doMouseSelection();
- }
- showCaret();
- }
- }
- if (select == false) {
- clearSelection(true);
- }
-}
-/**
- * Updates the selection based on the caret position
- */
-void doMouseSelection() {
- if (caretOffset <= selection.x ||
- (caretOffset > selection.x &&
- caretOffset < selection.y && selectionAnchor == selection.x)) {
- doSelection(SWT.LEFT);
- }
- else {
- doSelection(SWT.RIGHT);
- }
-}
-/**
- * Returns the offset of the word at the specified offset.
- * If the current selection extends from high index to low index
- * (i.e., right to left, or caret is at left border of selecton on
- * non-bidi platforms) the start offset of the word preceeding the
- * selection is returned. If the current selection extends from
- * low index to high index the end offset of the word following
- * the selection is returned.
- *
- * @param x mouse x location
- * @param newCaretOffset caret offset of the mouse cursor location
- * @param line line index of the mouse cursor location
- */
-int doMouseWordSelect(int x, int newCaretOffset, int line) {
- int wordOffset;
-
- // flip selection anchor based on word selection direction from
- // base double click. Always do this here (and don't rely on doAutoScroll)
- // because auto scroll only does not cover all possible mouse selections
- // (e.g., mouse x < 0 && mouse y > caret line y)
- if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
- selectionAnchor = doubleClickSelection.y;
- }
- else
- if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
- selectionAnchor = doubleClickSelection.x;
- }
- if (x >= 0 && x < getClientArea().width) {
- // find the previous/next word
- if (caretOffset == selection.x) {
- wordOffset = getWordStart(newCaretOffset);
- }
- else {
- wordOffset = getWordEndNoSpaces(newCaretOffset);
- }
- // mouse word select only on same line mouse cursor is on
- if (content.getLineAtOffset(wordOffset) == line) {
- newCaretOffset = wordOffset;
- }
- }
- return newCaretOffset;
-}
-/**
- * Scrolls one page down so that the last line (truncated or whole)
- * of the current page becomes the fully visible top line.
- * The caret is scrolled the same number of lines so that its location
- * relative to the top line remains the same. The exception is the end
- * of the text where a full page scroll is not possible. In this case
- * the caret is moved after the last character.
- * <p>
- *
- * @param select whether or not to select the page
- */
-void doPageDown(boolean select) {
- int lineCount = content.getLineCount();
- int oldColumnX = columnX;
- int caretLine;
-
- // do nothing if in single line mode. fixes 5673
- if (isSingleLine()) {
- return;
- }
- caretLine = getCaretLine();
- if (caretLine < lineCount - 1) {
- int verticalMaximum = lineCount * getVerticalIncrement();
- int pageSize = getClientArea().height;
- int scrollLines = Math.min(lineCount - caretLine - 1, getLineCountWhole());
- int scrollOffset;
-
- // ensure that scrollLines never gets negative and at leat one
- // line is scrolled. fixes bug 5602.
- scrollLines = Math.max(1, scrollLines);
- caretLine += scrollLines;
- if (isBidi()) {
- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
- }
- else {
- caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
- }
- if (select) {
- doSelection(SWT.RIGHT);
- }
- // scroll one page down or to the bottom
- scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
- if (scrollOffset + pageSize > verticalMaximum) {
- scrollOffset = verticalMaximum - pageSize;
- }
- if (scrollOffset > verticalScrollOffset) {
- setVerticalScrollOffset(scrollOffset, true);
- }
- }
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- // save the original horizontal caret position
- columnX = oldColumnX;
-}
-/**
- * Moves the cursor to the end of the last fully visible line.
- */
-void doPageEnd() {
- // go to end of line if in single line mode. fixes 5673
- if (isSingleLine()) {
- doLineEnd();
- }
- else {
- int line = getBottomIndex();
- int bottomCaretOffset = content.getOffsetAtLine(line) + content.getLine(line).length();
-
- if (caretOffset < bottomCaretOffset) {
- caretOffset = bottomCaretOffset;
- showCaret();
- }
- }
-}
-/**
- * Moves the cursor to the beginning of the first fully visible line.
- */
-void doPageStart() {
- int topCaretOffset = content.getOffsetAtLine(topIndex);
-
- if (caretOffset > topCaretOffset) {
- caretOffset = topCaretOffset;
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(topIndex);
- }
-}
-/**
- * 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
- * of the text where a full page scroll is not possible. In this case the
- * caret is moved in front of the first character.
- */
-void doPageUp() {
- int oldColumnX = columnX;
- int caretLine = getCaretLine();
-
- if (caretLine > 0) {
- int scrollLines = Math.max(1, Math.min(caretLine, getLineCountWhole()));
- int scrollOffset;
-
- caretLine -= scrollLines;
- if (isBidi()) {
- caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
- }
- else {
- caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
- }
- // scroll one page up or to the top
- scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
- if (scrollOffset < verticalScrollOffset) {
- setVerticalScrollOffset(scrollOffset, true);
- }
- }
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- // save the original horizontal caret position
- columnX = oldColumnX;
-}
-/**
- * Updates the selection to extend to the current caret position.
- */
-void doSelection(int direction) {
- int redrawStart = -1;
- int redrawEnd = -1;
-
- if (selectionAnchor == -1) {
- selectionAnchor = selection.x;
- }
- if (direction == SWT.LEFT) {
- if (caretOffset < selection.x) {
- // grow selection
- redrawEnd = selection.x;
- redrawStart = selection.x = caretOffset;
- // check if selection has reversed direction
- if (selection.y != selectionAnchor) {
- redrawEnd = selection.y;
- selection.y = selectionAnchor;
- }
- }
- else // test whether selection actually changed. Fixes 1G71EO1
- if (selectionAnchor == selection.x && caretOffset < selection.y) {
- // caret moved towards selection anchor (left side of selection).
- // shrink selection
- redrawEnd = selection.y;
- redrawStart = selection.y = caretOffset;
- }
- }
- else {
- if (caretOffset > selection.y) {
- // grow selection
- redrawStart = selection.y;
- redrawEnd = selection.y = caretOffset;
- // check if selection has reversed direction
- if (selection.x != selectionAnchor) {
- redrawStart = selection.x;
- selection.x = selectionAnchor;
- }
- }
- else // test whether selection actually changed. Fixes 1G71EO1
- if (selectionAnchor == selection.y && caretOffset > selection.x) {
- // caret moved towards selection anchor (right side of selection).
- // shrink selection
- redrawStart = selection.x;
- redrawEnd = selection.x = caretOffset;
- }
- }
- if (redrawStart != -1 && redrawEnd != -1) {
- internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
- sendSelectionEvent();
- }
-}
-/**
- * Moves the caret to the next character or to the beginning of the
- * next line if the cursor is at the end of a line.
- */
-void doSelectionCursorNext() {
- int caretLine = getCaretLine();
- int lineOffset = content.getOffsetAtLine(caretLine);
- int offsetInLine = caretOffset - lineOffset;
-
- if (offsetInLine < content.getLine(caretLine).length()) {
- // Remember the last direction. Always update lastCaretDirection,
- // even though it's not used in non-bidi mode in order to avoid
- // extra methods.
- lastCaretDirection = ST.COLUMN_NEXT;
- caretOffset++;
- showCaret();
- }
- else
- if (caretLine < content.getLineCount() - 1 && isSingleLine() == false) {
- // only go to next line if not in single line mode. fixes 5673
- caretLine++;
- caretOffset = content.getOffsetAtLine(caretLine);
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- }
-}
-/**
- * Moves the caret to the previous character or to the end of the previous
- * line if the cursor is at the beginning of a line.
- */
-void doSelectionCursorPrevious() {
- int caretLine = getCaretLine();
- int lineOffset = content.getOffsetAtLine(caretLine);
- int offsetInLine = caretOffset - lineOffset;
-
- if (offsetInLine > 0) {
- // Remember the last direction. Always update lastCaretDirection,
- // even though it's not used in non-bidi mode in order to avoid
- // extra methods.
- lastCaretDirection = ST.COLUMN_PREVIOUS;
- caretOffset--;
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- }
- else
- if (caretLine > 0) {
- caretLine--;
- lineOffset = content.getOffsetAtLine(caretLine);
- caretOffset = lineOffset + content.getLine(caretLine).length();
- showCaret();
- }
-}
-/**
- * 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
- * last line.
- * Adjusts the selection according to the caret change. This can either add
- * to or subtract from the old selection, depending on the previous selection
- * direction.
- */
-void doSelectionLineDown() {
- int oldColumnX;
- int caretLine;
- int lineStartOffset;
-
- if (isSingleLine()) {
- return;
- }
- caretLine = getCaretLine();
- lineStartOffset = content.getOffsetAtLine(caretLine);
- // reset columnX on selection
- oldColumnX = columnX = getXAtOffset(
- content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
- if (caretLine == content.getLineCount() - 1) {
- caretOffset = content.getCharCount();
- }
- else {
- caretLine = doLineDown();
- }
- setMouseWordSelectionAnchor();
- // select first and then scroll to reduce flash when key
- // repeat scrolls lots of lines
- doSelection(SWT.RIGHT);
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- // save the original horizontal caret position
- columnX = oldColumnX;
-}
-/**
- * Moves the caret one line up and to the same character offset relative
- * to the beginning of the line. Moves the caret to the end of the new line
- * 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.
- * Adjusts the selection according to the caret change. This can either add
- * to or subtract from the old selection, depending on the previous selection
- * direction.
- */
-void doSelectionLineUp() {
- int oldColumnX;
- int caretLine = getCaretLine();
- int lineStartOffset = content.getOffsetAtLine(caretLine);
-
- // reset columnX on selection
- oldColumnX = columnX = getXAtOffset(
- content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
- if (caretLine == 0) {
- caretOffset = 0;
- }
- else {
- caretLine = doLineUp();
- }
- setMouseWordSelectionAnchor();
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- doSelection(SWT.LEFT);
- // save the original horizontal caret position
- columnX = oldColumnX;
-}
-/**
- * Scrolls one page down so that the last line (truncated or whole)
- * of the current page becomes the fully visible top line.
- * The caret is scrolled the same number of lines so that its location
- * relative to the top line remains the same. The exception is the end
- * of the text where a full page scroll is not possible. In this case
- * the caret is moved after the last character.
- * <p>
- * Adjusts the selection according to the caret change. This can either add
- * to or subtract from the old selection, depending on the previous selection
- * direction.
- * </p>
- */
-void doSelectionPageDown() {
- int oldColumnX;
- int caretLine = getCaretLine();
- int lineStartOffset = content.getOffsetAtLine(caretLine);
-
- // reset columnX on selection
- oldColumnX = columnX = getXAtOffset(
- content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
- doPageDown(true);
- columnX = oldColumnX;
-}
-/**
- * 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
- * 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>
- * Adjusts the selection according to the caret change. This can either add
- * to or subtract from the old selection, depending on the previous selection
- * direction.
- * </p>
- */
-void doSelectionPageUp() {
- int oldColumnX;
- int caretLine = getCaretLine();
- int lineStartOffset = content.getOffsetAtLine(caretLine);
-
- // reset columnX on selection
- oldColumnX = columnX = getXAtOffset(
- content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
- doPageUp();
- columnX = oldColumnX;
-}
-/**
- * Moves the caret to the end of the next word .
- */
-void doSelectionWordNext() {
- int newCaretOffset = getWordEnd(caretOffset);
-
- // don't change caret position if in single line mode and the cursor
- // would be on a different line. fixes 5673
- if (isSingleLine() == false ||
- content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) {
- lastCaretDirection = ST.COLUMN_NEXT;
- caretOffset = newCaretOffset;
- showCaret();
- }
-}
-/**
- * Moves the caret to the start of the previous word.
- */
-void doSelectionWordPrevious() {
- int caretLine;
-
- lastCaretDirection = ST.COLUMN_PREVIOUS;
- caretOffset = getWordStart(caretOffset);
- caretLine = content.getLineAtOffset(caretOffset);
- // word previous always comes from bottom line. when
- // wrapping lines, stay on bottom line when on line boundary
- if (wordWrap && caretLine < content.getLineCount() - 1 &&
- caretOffset == content.getOffsetAtLine(caretLine + 1)) {
- caretLine++;
- }
- showCaret(caretLine);
-}
-/**
- * Moves the caret to the end of the next word.
- * If a selection exists, move the caret to the end of the selection
- * and remove the selection.
- */
-void doWordNext() {
- if (selection.y - selection.x > 0) {
- int caretLine;
-
- caretOffset = selection.y;
- caretLine = getCaretLine();
- showCaret(caretLine);
- }
- else {
- doSelectionWordNext();
- }
-}
-/**
- * Moves the caret to the start of the previous word.
- * If a selection exists, move the caret to the start of the selection
- * and remove the selection.
- */
-void doWordPrevious() {
- if (selection.y - selection.x > 0) {
- int caretLine;
-
- caretOffset = selection.x;
- caretLine = getCaretLine();
- showCaret(caretLine);
- }
- else {
- doSelectionWordPrevious();
- }
-}
-/**
- * Draws the specified rectangle.
- * Draw directly without invalidating the affected area when clearBackground is
- * false.
- * <p>
- *
- * @param x the x position
- * @param y the y position
- * @param width the width
- * @param height the height
- * @param clearBackground true=clear the background by invalidating the requested
- * redraw area, false=draw the foreground directly without invalidating the
- * redraw area.
- */
-void draw(int x, int y, int width, int height, boolean clearBackground) {
- if (clearBackground) {
- redraw(x + leftMargin, y + topMargin, width, height, true);
- }
- else {
- int startLine = (y + verticalScrollOffset) / lineHeight;
- int endY = y + height;
- int paintYFromTopLine = (startLine - topIndex) * lineHeight;
- int topLineOffset = (topIndex * lineHeight - verticalScrollOffset);
- int paintY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling
- int lineCount = content.getLineCount();
- Color background = getBackground();
- Color foreground = getForeground();
- GC gc = getGC();
-
- if (isSingleLine()) {
- lineCount = 1;
- if (startLine > 1) {
- startLine = 1;
- }
- }
- for (int i = startLine; paintY < endY && i < lineCount; i++, paintY += lineHeight) {
- String line = content.getLine(i);
- renderer.drawLine(line, i, paintY, gc, background, foreground, clearBackground);
- }
- gc.dispose();
- }
-}
-/**
- * Ends the autoscroll process.
- */
-void endAutoScroll() {
- autoScrollDirection = SWT.NULL;
-}
-/**
- * @see org.eclipse.swt.widgets.Control#getBackground
- */
-public Color getBackground() {
- checkWidget();
- if (background == null) {
- return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
- }
- return background;
-}
-/**
- * Gets the BIDI coloring mode. When true the BIDI text display
- * algorithm is applied to segments of text that are the same
- * color.
- *
- * @return the current coloring mode
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * <p>
- * @deprecated use BidiSegmentListener instead.
- * </p>
- */
-public boolean getBidiColoring() {
- checkWidget();
- return bidiColoring;
-}
-/**
- * Returns the offset at the specified x location in the specified line.
- * Also sets the caret direction so that the caret is placed correctly
- * depending on whether the mouse location is in a R2L or L2R segment.
- * <p>
- *
- * @param x x location of the mouse location
- * @param line line the mouse location is in
- * @return the offset at the specified x location in the specified line,
- * relative to the beginning of the document
- */
-int getBidiOffsetAtMouseLocation(int x, int line) {
- String lineText = content.getLine(line);
- int lineOffset = content.getOffsetAtLine(line);
- GC gc = getGC();
- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
- int[] values;
- int offsetInLine;
- x += horizontalScrollOffset;
- values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin);
- offsetInLine = values[0];
- lastCaretDirection = values[1];
- gc.dispose();
-
- return lineOffset + offsetInLine;
-}
-/**
- * Returns the x position of the character at the specified offset
- * relative to the first character in the line.
- * </p>
- *
- * @param text text to be measured.
- * @param endOffset offset of the character
- * @param bidi the bidi object to use for measuring text in bidi
- * locales.
- * @return x position of the character at the specified offset.
- * 0 if the length is outside the specified text.
- */
-int getBidiTextPosition(String text, int endOffset, StyledTextBidi bidi) {
- if (endOffset > text.length()) {
- return 0;
- }
- // Use lastCaretDirection in order to get same results as during
- // caret positioning (setBidiCaretLocation). Fixes 1GKU4C5.
- return bidi.getTextPosition(endOffset, lastCaretDirection);
-}
-/**
- * Returns the index of the last fully visible line.
- * <p>
- *
- * @return index of the last fully visible line.
- */
-int getBottomIndex() {
- int lineCount = 1;
-
- if (lineHeight != 0) {
- // calculate the number of lines that are fully visible
- int partialTopLineHeight = topIndex * lineHeight - verticalScrollOffset;
- lineCount = (getClientArea().height - partialTopLineHeight) / lineHeight;
- }
- return Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
-}
-/**
- * Returns the caret position relative to the start of the text.
- * <p>
- *
- * @return the caret position relative to the start of the text.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getCaretOffset() {
- checkWidget();
-
- return caretOffset;
-}
-/**
- * Returns the caret offset at the given x location in the line.
- * The caret offset is the offset of the character where the caret will be
- * placed when a mouse click occurs. The caret offset will be the offset of
- * the character after the clicked one if the mouse click occurs at the second
- * half of a character.
- * Doesn't properly handle ligatures and other context dependent characters
- * unless the current locale is a bidi locale.
- * Ligatures are handled properly as long as they don't occur at lineXOffset.
- * <p>
- *
- * @param line text of the line to calculate the offset in
- * @param lineOffset offset of the first character in the line.
- * 0 based from the beginning of the document.
- * @param lineXOffset x location in the line
- * @return caret offset at the x location relative to the start of the line.
- */
-int getCaretOffsetAtX(String line, int lineOffset, int lineXOffset) {
- int offset = 0;
- GC gc = getGC();
- StyleRange[] styles = null;
- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
-
- lineXOffset += horizontalScrollOffset;
- if (event != null) {
- styles = renderer.filterLineStyles(event.styles);
- }
- int low = -1;
- int high = line.length();
- while (high - low > 1) {
- offset = (high + low) / 2;
- int x = renderer.getTextPosition(line, lineOffset, offset, styles, gc) + leftMargin;
- int charWidth = renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc) + leftMargin - x;
- if (lineXOffset <= x + charWidth / 2) {
- high = offset;
- }
- else {
- low = offset;
- }
- }
- offset = high;
- gc.dispose();
- return offset;
-}
-/**
- * Returns the caret width.
- * <p>
- *
- * @return the caret width, 0 if caret is null.
- */
-int getCaretWidth() {
- Caret caret = getCaret();
- if (caret == null) return 0;
- return caret.getSize().x;
-}
-/**
- * Returns the content implementation that is used for text storage
- * or null if no user defined content implementation has been set.
- * <p>
- *
- * @return content implementation that is used for text storage or null
- * if no user defined content implementation has been set.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public StyledTextContent getContent() {
- checkWidget();
-
- return logicalContent;
-}
-/**
- * Returns whether the widget implements double click mouse behavior.
- * <p>
- *
- * @return true if double clicking a word selects the word, false if double clicks
- * have the same effect as regular mouse clicks
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public boolean getDoubleClickEnabled() {
- checkWidget();
- return doubleClickEnabled;
-}
-/**
- * Returns whether the widget content can be edited.
- * <p>
- *
- * @return true if content can be edited, false otherwise
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public boolean getEditable() {
- checkWidget();
- return editable;
-}
-/**
- * @see org.eclipse.swt.widgets.Control#getForeground
- */
-public Color getForeground() {
- checkWidget();
- if (foreground == null) {
- return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
- }
- return foreground;
-}
-/**
- * Return a GC to use for rendering and update the cached font style to
- * represent the current style.
- * <p>
- *
- * @return GC.
- */
-GC getGC() {
- renderer.setCurrentFontStyle(SWT.NORMAL);
- return new GC(this);
-}
-/**
- * Returns the horizontal scroll increment.
- * <p>
- *
- * @return horizontal scroll increment.
- */
-int getHorizontalIncrement() {
- GC gc = getGC();
- int increment = gc.getFontMetrics().getAverageCharWidth();
-
- gc.dispose();
- return increment;
-}
-/**
- * Returns the horizontal scroll offset relative to the start of the line.
- * <p>
- *
- * @return horizontal scroll offset relative to the start of the line,
- * measured in character increments starting at 0, if > 0 the content is scrolled
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getHorizontalIndex() {
- checkWidget();
- return horizontalScrollOffset / getHorizontalIncrement();
-}
-/**
- * Returns the horizontal scroll offset relative to the start of the line.
- * <p>
- *
- * @return the horizontal scroll offset relative to the start of the line,
- * measured in pixel starting at 0, if > 0 the content is scrolled.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getHorizontalPixel() {
- checkWidget();
- return horizontalScrollOffset;
-}
-/**
- * Returns the action assigned to the key.
- * Returns SWT.NULL if there is no action associated with the key.
- * <p>
- *
- * @param key a key code defined in SWT.java or a character.
- * Optionally ORd with a state mask. Preferred state masks are one or more of
- * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
- * differences. However, there may be cases where using the specific state masks
- * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
- * @return one of the predefined actions defined in ST.java or SWT.NULL
- * if there is no action associated with the key.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getKeyBinding(int key) {
- checkWidget();
- Integer action = (Integer) keyActionMap.get(new Integer(key));
- int intAction;
-
- if (action == null) {
- intAction = SWT.NULL;
- }
- else {
- intAction = action.intValue();
- }
- return intAction;
-}
-/**
- * Gets the number of characters.
- * <p>
- *
- * @return number of characters in the widget
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getCharCount() {
- checkWidget();
- return content.getCharCount();
-}
-/**
- * Returns the background color of the line at the given index.
- * Returns null if a LineBackgroundListener has been set or if no background
- * 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.
- * <p>
- *
- * @return the background color of the line at the given index.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
- * </ul>
- */
-public Color getLineBackground(int index) {
- checkWidget();
- Color lineBackground = null;
-
- if (index < 0 || index > logicalContent.getLineCount()) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (userLineBackground == false) {
- lineBackground = defaultLineStyler.getLineBackground(index);
- }
- return lineBackground;
-}
-/**
- * Returns the line background data for the given line or null if
- * there is none.
- * <p>
- * @param lineOffset offset of the line start relative to the start
- * of the content.
- * @param line line to get line background data for
- * @return line background data for the given line.
- */
-StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
- return sendLineEvent(LineGetBackground, lineOffset, line);
-}
-/**
- * Gets the number of text lines.
- * <p>
- *
- * @return the number of lines in the widget
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getLineCount() {
- checkWidget();
- return getLineAtOffset(getCharCount()) + 1;
-}
-/**
- * Returns the number of lines that can be completely displayed in the
- * widget client area.
- * <p>
- *
- * @return number of lines that can be completely displayed in the widget
- * client area.
- */
-int getLineCountWhole() {
- int lineCount;
-
- if (lineHeight != 0) {
- lineCount = getClientArea().height / lineHeight;
- }
- else {
- lineCount = 1;
- }
- return lineCount;
-}
-/**
- * Returns the line at the specified offset in the text.
- * 0 <= offset <= getCharCount() so that getLineAtOffset(getCharCount())
- * returns the line of the insert location.
- * <p>
- *
- * @param offset offset relative to the start of the content.
- * 0 <= offset <= getCharCount()
- * @return line at the specified offset in the text
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
- * </ul>
- */
-public int getLineAtOffset(int offset) {
- checkWidget();
-
- if (offset < 0 || offset > getCharCount()) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- return logicalContent.getLineAtOffset(offset);
-}
-/**
- * Returns the line delimiter used for entering new lines by key down
- * or paste operation.
- * <p>
- *
- * @return line delimiter used for entering new lines by key down
- * or paste operation.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public String getLineDelimiter() {
- checkWidget();
- return content.getLineDelimiter();
-}
-/**
- * Returns a StyledTextEvent that can be used to request data such
- * as styles and background color for a line.
- * The specified line may be a visual (wrapped) line if in word
- * wrap mode. The returned object will always be for a logical
- * (unwrapped) line.
- * <p>
- *
- * @param lineOffset offset of the line. This may be the offset of
- * a visual line if the widget is in word wrap mode.
- * @param line line text. This may be the text of a visualline if
- * the widget is in word wrap mode.
- * @return StyledTextEvent that can be used to request line data
- * for the given line.
- */
-StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
- StyledTextEvent event = null;
-
- if (isListening(eventType)) {
- event = new StyledTextEvent(logicalContent);
- if (wordWrap) {
- // if word wrap is on, the line offset and text may be visual (wrapped)
- int lineIndex = logicalContent.getLineAtOffset(lineOffset);
-
- event.detail = logicalContent.getOffsetAtLine(lineIndex);
- event.text = logicalContent.getLine(lineIndex);
- }
- else {
- event.detail = lineOffset;
- event.text = line;
- }
- notifyListeners(eventType, event);
- }
- return event;
-}
-/**
- * Returns the line height.
- * <p>
- *
- * @return line height in pixel.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getLineHeight() {
- checkWidget();
- return lineHeight;
-}
-/**
- * Returns a LineCache implementation. Depending on whether or not
- * word wrap is on this may be a line wrapping or line width
- * calculating implementaiton.
- * <p>
- *
- * @param content StyledTextContent to create the LineCache on.
- * @return a LineCache implementation
- */
-LineCache getLineCache(StyledTextContent content) {
- LineCache lineCache;
-
- if (wordWrap) {
- lineCache = new WordWrapCache(this, (WrappedContent) content);
- }
- else {
- lineCache = new ContentWidthCache(this, content.getLineCount());
- }
- return lineCache;
-}
-/**
- * Returns the line style data for the given line or null if there is
- * none. If there is a LineStyleListener but it does not set any styles,
- * the StyledTextEvent.styles field will be initialized to an empty
- * array.
- * <p>
- *
- * @param lineOffset offset of the line start relative to the start of
- * the content.
- * @param line line to get line styles for
- * @return line style data for the given line. Styles may start before
- * line start and end after line end
- */
-StyledTextEvent getLineStyleData(int lineOffset, String line) {
- return sendLineEvent(LineGetStyle, lineOffset, line);
-}
-/**
- * Returns the x, y location of the upper left corner of the character
- * bounding box at the specified offset in the text. The point is
- * relative to the upper left corner of the widget client area.
- * <p>
- *
- * @param offset offset relative to the start of the content.
- * 0 <= offset <= getCharCount()
- * @return x, y location of the upper left corner of the character
- * bounding box at the specified offset in the text.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
- * </ul>
- */
-public Point getLocationAtOffset(int offset) {
- checkWidget();
- if (offset < 0 || offset > getCharCount()) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- int line = content.getLineAtOffset(offset);
- int lineOffset = content.getOffsetAtLine(line);
- String lineContent = content.getLine(line);
- int x = getXAtOffset(lineContent, line, offset - lineOffset);
- int y = line * lineHeight - verticalScrollOffset;
-
- return new Point(x, y);
-}
-/**
- * Returns the character offset of the first character of the given line.
- * <p>
- *
- * @param lineIndex index of the line, 0 based relative to the first
- * line in the content. 0 <= lineIndex < getLineCount(), except
- * lineIndex may always be 0
- * @return offset offset of the first character of the line, relative to
- * the beginning of the document. The first character of the document is
- * at offset 0.
- * When there are not any lines, getOffsetAtLine(0) is a valid call that
- * answers 0.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
- * </ul>
- * @since 2.0
- */
-public int getOffsetAtLine(int lineIndex) {
- checkWidget();
-
- if (lineIndex < 0 ||
- (lineIndex > 0 && lineIndex >= logicalContent.getLineCount())) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- return logicalContent.getOffsetAtLine(lineIndex);
-}
-/**
- * Returns the offset of the character at the given location relative
- * to the first character in the document.
- * The return value reflects the character offset that the caret will
- * be placed at if a mouse click occurred at the specified location.
- * If the x coordinate of the location is beyond the center of a character
- * the returned offset will be behind the character.
- * <p>
- *
- * @param point the origin of character bounding box relative to
- * the origin of the widget client area.
- * @return offset of the character at the given location relative
- * to the first character in the document.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when point is null</li>
- * <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
- * </ul>
- */
-public int getOffsetAtLocation(Point point) {
- checkWidget();
- int line;
- int lineOffset;
- int offsetInLine;
- String lineText;
-
- if (point == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- // is y above first line or is x before first column?
- if (point.y + verticalScrollOffset < 0 || point.x + horizontalScrollOffset < 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- line = (getTopPixel() + point.y) / lineHeight;
- // does the referenced line exist?
- if (line >= content.getLineCount()) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- lineText = content.getLine(line);
- lineOffset = content.getOffsetAtLine(line);
- offsetInLine = getOffsetAtX(lineText, lineOffset, point.x);
- // is the x position within the line?
- if (offsetInLine == -1) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- return lineOffset + offsetInLine;
-}
-/**
- * Returns the offset at the specified x location in the specified line.
- * <p>
- *
- * @param x x location of the mouse location
- * @param line line the mouse location is in
- * @return the offset at the specified x location in the specified line,
- * relative to the beginning of the document
- */
-int getOffsetAtMouseLocation(int x, int line) {
- String lineText = content.getLine(line);
- int lineOffset = content.getOffsetAtLine(line);
- int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x);
- return lineOffset + offsetInLine;
-}
-/**
- * Returns the offset of the character at the given x location in the line.
- * <p>
- *
- * @param line text of the line to calculate the offset in
- * @param lineOffset offset of the first character in the line.
- * 0 based from the beginning of the document.
- * @param lineXOffset x location in the line
- * @return offset of the character at the x location relative to the start
- * of the line. -1 if the x location is past the end if the line.
- */
-int getOffsetAtX(String line, int lineOffset, int lineXOffset) {
- GC gc = getGC();
- int offset;
-
- lineXOffset += (horizontalScrollOffset - leftMargin);
- if (isBidi()) {
- StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
- offset = bidi.getOffsetAtX(lineXOffset);
- }
- else {
- StyleRange[] styles = null;
- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
-
- if (event != null) {
- styles = renderer.filterLineStyles(event.styles);
- }
- int low = -1;
- int high = line.length();
- while (high - low > 1) {
- offset = (high + low) / 2;
- // Restrict right/high search boundary only if x is within searched text segment.
- // Fixes 1GL4ZVE.
- if (lineXOffset < renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc)) {
- high = offset;
- }
- else
- if (high == line.length() && high - offset == 1) {
- // requested x location is past end of line
- high = -1;
- }
- else {
- low = offset;
- }
- }
- offset = high;
- }
- gc.dispose();
- return offset;
-}
-/**
- * Returns the index of the last partially visible line.
- *
- * @return index of the last partially visible line.
- */
-int getPartialBottomIndex() {
- int partialLineCount = Compatibility.ceil(getClientArea().height, lineHeight);
- return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1;
-}
-/**
- * Returns the content in the specified range using the platform line
- * delimiter to separate lines.
- * <p>
- *
- * @param writer the TextWriter to write line text into
- * @return the content in the specified range using the platform line
- * delimiter to separate lines as written by the specified TextWriter.
- */
-String getPlatformDelimitedText(TextWriter writer) {
- int end = writer.getStart() + writer.getCharCount();
- int startLine = logicalContent.getLineAtOffset(writer.getStart());
- int endLine = logicalContent.getLineAtOffset(end);
- String endLineText = logicalContent.getLine(endLine);
- int endLineOffset = logicalContent.getOffsetAtLine(endLine);
-
- for (int i = startLine; i <= endLine; i++) {
- writer.writeLine(logicalContent.getLine(i), logicalContent.getOffsetAtLine(i));
- if (i < endLine) {
- writer.writeLineDelimiter(PlatformLineDelimiter);
- }
- }
- if (end > endLineOffset + endLineText.length()) {
- writer.writeLineDelimiter(PlatformLineDelimiter);
- }
- writer.close();
- return writer.toString();
-}
-/**
- * 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,
- * ranging from 0..N
- * <p>
- *
- * @return start and end of the selection, x is the offset of the first
- * selected character, y is the offset after the last selected character.
- * The selection values returned are visual (i.e., x will always always be
- * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right
- * (LtoR), compare the caretOffset to the start and end of the selection
- * (e.g., caretOffset == start of selection implies that the selection is RtoL).
- * @see #getSelectionRange
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public Point getSelection() {
- checkWidget();
- return new Point(selection.x, selection.y);
-}
-/**
- * Returns the selection.
- * <p>
- *
- * @return start and length of the selection, x is the offset of the
- * first selected character, relative to the first character of the
- * widget content. y is the length of the selection.
- * The selection values returned are visual (i.e., length will always always be
- * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right
- * (LtoR), compare the caretOffset to the start and end of the selection
- * (e.g., caretOffset == start of selection implies that the selection is RtoL).
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public Point getSelectionRange() {
- checkWidget();
- return new Point(selection.x, selection.y - selection.x);
-}
-/**
- * Returns the receiver's selection background color.
- *
- * @return the selection background color
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @since 2.1
- */
-public Color getSelectionBackground() {
- checkWidget();
- if (selectionBackground == null) {
- return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
- }
- return selectionBackground;
-}
-/**
- * Gets the number of selected characters.
- * <p>
- *
- * @return the number of selected characters.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getSelectionCount() {
- checkWidget();
- return getSelectionRange().y;
-}
-/**
- * Returns the receiver's selection foreground color.
- *
- * @return the selection foreground color
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @since 2.1
- */
-public Color getSelectionForeground() {
- checkWidget();
- if (selectionForeground == null) {
- return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
- }
- return selectionForeground;
-}
-/**
- * Returns the selected text.
- * <p>
- *
- * @return selected text, or an empty String if there is no selection.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public String getSelectionText() {
- checkWidget();
- return content.getTextRange(selection.x, selection.y - selection.x);
-}
-/**
- * Returns the text segments that should be treated as if they
- * had a different direction than the surrounding text.
- * <p>
- *
- * @param lineOffset offset of the first character in the line.
- * 0 based from the beginning of the document.
- * @param line text of the line to specify bidi segments for
- * @return text segments that should be treated as if they had a
- * different direction than the surrounding text. Only the start
- * index of a segment is specified, relative to the start of the
- * line. Always starts with 0 and ends with the line length.
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
- * by the listener do not start with 0, are not in ascending order,
- * exceed the line length or have duplicates</li>
- * </ul>
- */
-int [] getBidiSegments(int lineOffset, String line) {
- if (isListening(LineGetSegments) == false) {
- return getBidiSegmentsCompatibility(line, lineOffset);
- }
- StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
- int lineLength = line.length();
- int[] segments;
- if (event == null || event.segments == null || event.segments.length == 0) {
- segments = new int[] {0, lineLength};
- }
- else {
- int segmentCount = event.segments.length;
-
- // test segment index consistency
- if (event.segments[0] != 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- for (int i = 1; i < segmentCount; i++) {
- if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- }
- // ensure that last segment index is line end offset
- if (event.segments[segmentCount - 1] != lineLength) {
- segments = new int[segmentCount + 1];
- System.arraycopy(event.segments, 0, segments, 0, segmentCount);
- segments[segmentCount] = lineLength;
- }
- else {
- segments = event.segments;
- }
- }
- return segments;
-}
-/**
- * @see getBidiSegments
- * Supports deprecated setBidiColoring API. Remove when API is removed.
- */
-int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
- StyledTextEvent event;
- StyleRange [] styles = new StyleRange [0];
- int lineLength = line.length();
- if (bidiColoring == false) {
- return new int[] {0, lineLength};
- }
- event = renderer.getLineStyleData(lineOffset, line);
- if (event != null) {
- styles = event.styles;
- }
- if (styles.length == 0) {
- return new int[] {0, lineLength};
- }
- int k=0, count = 1;
- while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
- k++;
- }
- int[] offsets = new int[(styles.length - k) * 2 + 2];
- for (int i = k; i < styles.length; i++) {
- StyleRange style = styles[i];
- int styleLineStart = Math.max(style.start - lineOffset, 0);
- int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
- styleLineEnd = Math.min (styleLineEnd, line.length ());
- if (i > 0 && count > 1 &&
- ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
- (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
- style.similarTo(styles[i-1])) {
- offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
- offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
- } else {
- if (styleLineStart > offsets[count - 1]) {
- offsets[count] = styleLineStart;
- count++;
- }
- offsets[count] = styleLineEnd;
- count++;
- }
- }
- // add offset for last non-colored segment in line, if any
- if (lineLength > offsets[count-1]) {
- offsets [count] = lineLength;
- count++;
- }
- if (count == offsets.length) {
- return offsets;
- }
- int [] result = new int [count];
- System.arraycopy (offsets, 0, result, 0, count);
- return result;
-}
-/**
- * Returns the style range at the given offset.
- * Returns null if a LineStyleListener has been set or if a style is not set
- * for the offset.
- * Should not be called if a LineStyleListener has been set since the
- * listener maintains the styles.
- * <p>
- *
- * @param offset the offset to return the style for.
- * 0 <= offset < getCharCount() must be true.
- * @return a StyleRange with start == offset and length == 1, indicating
- * the style at the given offset. null if a LineStyleListener has been set
- * or if a style is not set for the given offset.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
- * </ul>
- */
-public StyleRange getStyleRangeAtOffset(int offset) {
- checkWidget();
- if (offset < 0 || offset >= getCharCount()) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (userLineStyle == false) {
- return defaultLineStyler.getStyleRangeAtOffset(offset);
- }
- return null;
-}
-/**
- * Returns the styles.
- * Returns an empty array if a LineStyleListener has been set.
- * Should not be called if a LineStyleListener has been set since the
- * listener maintains the styles.
- * <p>
- *
- * @return the styles or null if a LineStyleListener has been set.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public StyleRange [] getStyleRanges() {
- checkWidget();
- StyleRange styles[];
-
- if (userLineStyle == false) {
- styles = defaultLineStyler.getStyleRanges();
- }
- else {
- styles = new StyleRange[0];
- }
- return styles;
-}
-/**
- * Returns a StyledTextBidi object for the specified line.
- * <p>
- *
- * @param lineText the line that the StyledTextBidi object should
- * work on.
- * @param lineOffset offset of the beginning of the line, relative
- * to the beginning of the document
- * @param gc GC to use when creating a new StyledTextBidi object.
- * @return a StyledTextBidi object for the specified line.
- */
-StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc) {
- return getStyledTextBidi(lineText, lineOffset, gc, null);
-}
-/**
- * Returns a StyledTextBidi object for the specified line.
- * <p>
- *
- * @param lineText the line that the StyledTextBidi object should
- * work on.
- * @param lineOffset offset of the beginning of the line, relative
- * to the beginning of the document
- * @param gc GC to use when creating a new StyledTextBidi object.
- * @param styles StyleRanges to use when creating a new StyledTextBidi
- * object.
- * @return a StyledTextBidi object for the specified line.
- */
-StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc, StyleRange[] styles) {
- return renderer.getStyledTextBidi(lineText, lineOffset, gc, styles);
-}
-/**
- * Returns the tab width measured in characters.
- *
- * @return tab width measured in characters
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getTabs() {
- checkWidget();
- return tabLength;
-}
-/**
- * Returns a copy of the widget content.
- * <p>
- *
- * @return copy of the widget content
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public String getText() {
- checkWidget();
- return content.getTextRange(0, getCharCount());
-}
-/**
- * Returns the widget content between the two offsets.
- * <p>
- *
- * @param start offset of the first character in the returned String
- * @param end offset of the last character in the returned String
- * @return widget content starting at start and ending at end
- * @see #getTextRange(int,int)
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
- * </ul>
- */
-public String getText(int start, int end) {
- checkWidget();
- int contentLength = getCharCount();
-
- if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- return content.getTextRange(start, end - start + 1);
-}
-/**
- * Returns the widget content starting at start for length characters.
- * <p>
- *
- * @param start offset of the first character in the returned String
- * @param length number of characters to return
- * @return widget content starting at start and extending length characters.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
- * </ul>
- */
-public String getTextRange(int start, int length) {
- checkWidget();
- int contentLength = getCharCount();
- int end = start + length;
-
- if (start > end || start < 0 || end > contentLength) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- return content.getTextRange(start, length);
-}
-/**
- * Gets the text limit. The text limit specifies the amount of text that the user
- * can type into the widget.
- * <p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getTextLimit() {
- checkWidget();
-
- return textLimit;
-}
-/**
- * Returns the x position of the character at the specified offset
- * relative to the first character in the line.
- * Expands tabs to tab stops using the widget tab width.
- * <p>
- *
- * @param line line to be measured.
- * @param lineIndex index of the line relative to the first kine of the
- * document
- * @param length number of characters to measure. Tabs are counted
- * as one character in this parameter.
- * @param gc GC to use for measuring text
- * @return x position of the character at the specified offset
- * with tabs expanded to tab stops. 0 if the length is outside the
- * specified text.
- */
-int getTextPosition(String line, int lineIndex, int length, GC gc) {
- int lineOffset = content.getOffsetAtLine(lineIndex);
- int lineLength = line.length();
- int width;
- if (lineLength == 0 || length > lineLength) {
- return 0;
- }
- if (isBidi()) {
- StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc, null);
- width = getBidiTextPosition(line, length, bidi);
- }
- else {
- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
- StyleRange[] styles = null;
- if (event != null) {
- styles = renderer.filterLineStyles(event.styles);
- }
- width = renderer.getTextPosition(line, lineOffset, length, styles, gc);
- }
- return width;
-}
-/**
- * Gets the top index. The top index is the index of the fully visible line that
- * is currently at the top of the widget or the topmost partially visible line if
- * no line is fully visible.
- * The top index changes when the widget is scrolled. Indexing is zero based.
- * <p>
- *
- * @return the index of the top line
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getTopIndex() {
- checkWidget();
- int logicalTopIndex = topIndex;
-
- if (wordWrap) {
- int visualLineOffset = content.getOffsetAtLine(topIndex);
- logicalTopIndex = logicalContent.getLineAtOffset(visualLineOffset);
- }
- return logicalTopIndex;
-}
-/**
- * Gets the top pixel. The top pixel is the pixel position of the line that is
- * currently at the top of the widget.The text widget can be scrolled by pixels
- * by dragging the scroll thumb so that a partial line may be displayed at the top
- * the widget. The top pixel changes when the widget is scrolled. The top pixel
- * does not include the widget trimming.
- * <p>
- *
- * @return pixel position of the top line
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getTopPixel() {
- checkWidget();
- return verticalScrollOffset;
-}
-/**
- * Returns the vertical scroll increment.
- * <p>
- *
- * @return vertical scroll increment.
- */
-int getVerticalIncrement() {
- return lineHeight;
-}
-/**
- * Returns the index of the line the caret is on.
- * When in word wrap mode and at the end of one wrapped line/
- * beginning of the continuing wrapped line the caret offset
- * is not sufficient to determine the caret line.
- *
- * @return the index of the line the caret is on.
- */
-int getCaretLine() {
- int caretLine = content.getLineAtOffset(caretOffset);
- int leftColumnX = 0;
-
- if (isBidi()) {
- leftColumnX = XINSET;
- }
- if (wordWrap && columnX <= leftColumnX &&
- caretLine < content.getLineCount() - 1 &&
- caretOffset == content.getOffsetAtLine(caretLine + 1)) {
- caretLine++;
- }
- return caretLine;
-}
-/**
- * Returns the offset of the character after the word at the specified
- * offset.
- * <p>
- * There are two classes of words formed by a sequence of characters:
- * <ul>
- * <li>from 0-9 and A-z (ASCII 48-57 and 65-122)
- * <li>every other character except line breaks
- * </ul>
- * </p>
- * <p>
- * Space characters ' ' (ASCII 20) are special as they are treated as
- * part of the word leading up to the space character. Line breaks are
- * treated as one word.
- * </p>
- */
-int getWordEnd(int offset) {
- int line = logicalContent.getLineAtOffset(offset);
- int lineOffset = logicalContent.getOffsetAtLine(line);
- String lineText = logicalContent.getLine(line);
- int lineLength = lineText.length();
-
- if (offset >= getCharCount()) {
- return offset;
- }
- if (offset == lineOffset + lineLength) {
- line++;
- offset = logicalContent.getOffsetAtLine(line);
- }
- else {
- offset -= lineOffset;
- char ch = lineText.charAt(offset);
- boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
- while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit) {
- offset++;
- ch = lineText.charAt(offset);
- }
- // skip over trailing whitespace
- while (offset < lineLength - 1 && Compatibility.isSpaceChar(ch)) {
- offset++;
- ch = lineText.charAt(offset);
- }
- if (offset == lineLength - 1 && (Compatibility.isLetterOrDigit(ch) == letterOrDigit || Compatibility.isSpaceChar(ch))) {
- offset++;
- }
- offset += lineOffset;
- }
- return offset;
-}
-/**
- * Returns the offset of the character after the word at the specified
- * offset.
- * <p>
- * There are two classes of words formed by a sequence of characters:
- * <ul>
- * <li>from 0-9 and A-z (ASCII 48-57 and 65-122)
- * <li>every other character except line breaks
- * </ul>
- * </p>
- * <p>
- * Spaces are ignored and do not represent a word. Line breaks are treated
- * as one word.
- * </p>
- */
-int getWordEndNoSpaces(int offset) {
- int line = logicalContent.getLineAtOffset(offset);
- int lineOffset = logicalContent.getOffsetAtLine(line);
- String lineText = logicalContent.getLine(line);
- int lineLength = lineText.length();
-
- if (offset >= getCharCount()) {
- return offset;
- }
- if (offset == lineOffset + lineLength) {
- line++;
- offset = logicalContent.getOffsetAtLine(line);
- }
- else {
- offset -= lineOffset;
- char ch = lineText.charAt(offset);
- boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
-
- while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
- offset++;
- ch = lineText.charAt(offset);
- }
- if (offset == lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
- offset++;
- }
- offset += lineOffset;
- }
- return offset;
-}
-/**
- * Returns the start offset of the word at the specified offset.
- * There are two classes of words formed by a sequence of characters:
- * <p>
- * <ul>
- * <li>from 0-9 and A-z (ASCII 48-57 and 65-122)
- * <li>every other character except line breaks
- * </ul>
- * </p>
- * <p>
- * Space characters ' ' (ASCII 20) are special as they are treated as
- * part of the word leading up to the space character. Line breaks are treated
- * as one word.
- * </p>
- */
-int getWordStart(int offset) {
- int line = logicalContent.getLineAtOffset(offset);
- int lineOffset = logicalContent.getOffsetAtLine(line);
- String lineText = logicalContent.getLine(line);
-
- if (offset <= 0) {
- return offset;
- }
- if (offset == lineOffset) {
- line--;
- lineText = logicalContent.getLine(line);
- offset = logicalContent.getOffsetAtLine(line) + lineText.length();
- }
- else {
- char ch;
- boolean letterOrDigit;
-
- offset -= lineOffset;
- // skip over trailing whitespace
- do {
- offset--;
- ch = lineText.charAt(offset);
- } while (offset > 0 && Compatibility.isSpaceChar(ch));
- letterOrDigit = Compatibility.isLetterOrDigit(ch);
- while (offset > 0 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
- offset--;
- ch = lineText.charAt(offset);
- }
- if (offset > 0 || Compatibility.isLetterOrDigit(ch) != letterOrDigit) {
- offset++;
- }
- offset += lineOffset;
- }
- return offset;
-}
-/**
- * Returns whether the widget wraps lines.
- * <p>
- *
- * @return true if widget wraps lines, false otherwise
- * @since 2.0
- */
-public boolean getWordWrap() {
- checkWidget();
- return wordWrap;
-}
-/**
- * Returns the x location of the character at the give offset in the line.
- * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
- * <p>
- *
- * @return x location of the character at the given offset in the line.
- */
-int getXAtOffset(String line, int lineIndex, int lineOffset) {
- int x;
- if (lineOffset == 0 && isBidi() == false) {
- x = leftMargin;
- }
- else {
- GC gc = getGC();
- x = getTextPosition(line, lineIndex, Math.min(line.length(), lineOffset), gc) + leftMargin;
- gc.dispose();
- if (lineOffset > line.length()) {
- // offset is not on the line. return an x location one character
- // after the line to indicate the line delimiter.
- x += lineEndSpaceWidth;
- }
- }
- return x - horizontalScrollOffset;
-}
-/**
- * Inserts a string. The old selection is replaced with the new text.
- * <p>
- *
- * @param string the string
- * @see #replaceTextRange(int,int,String)
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when string is null</li>
- * </ul>
- */
-public void insert(String string) {
- checkWidget();
- if (string == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- Point sel = getSelectionRange();
- replaceTextRange(sel.x, sel.y, string);
-}
-/**
- * Creates content change listeners and set the default content model.
- */
-void installDefaultContent() {
- textChangeListener = new TextChangeListener() {
- public void textChanging(TextChangingEvent event) {
- handleTextChanging(event);
- }
- public void textChanged(TextChangedEvent event) {
- handleTextChanged(event);
- }
- public void textSet(TextChangedEvent event) {
- handleTextSet(event);
- }
- };
- logicalContent = content = new DefaultContent();
- content.addTextChangeListener(textChangeListener);
-}
-/**
- * Creates a default line style listener.
- * Used to store line background colors and styles.
- * Removed when the user sets a LineStyleListener.
- * <p>
- *
- * @see #addLineStyleListener
- */
-void installDefaultLineStyler() {
- defaultLineStyler = new DefaultLineStyler(logicalContent);
- StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
- if (userLineStyle == false) {
- addListener(LineGetStyle, typedListener);
- }
- if (userLineBackground == false) {
- addListener(LineGetBackground, typedListener);
- }
-}
-/**
- * Adds event listeners
- */
-void installListeners() {
- ScrollBar verticalBar = getVerticalBar();
- ScrollBar horizontalBar = getHorizontalBar();
-
- addListener(SWT.Dispose, new Listener() {
- public void handleEvent(Event event) {
- handleDispose();
- }
- });
- addListener(SWT.KeyDown, new Listener() {
- public void handleEvent(Event event) {
- handleKeyDown(event);
- }
- });
- addListener(SWT.MouseDown, new Listener() {
- public void handleEvent(Event event) {
- handleMouseDown(event);
- }
- });
- addListener(SWT.MouseUp, new Listener() {
- public void handleEvent(Event event) {
- handleMouseUp(event);
- }
- });
- addListener(SWT.MouseDoubleClick, new Listener() {
- public void handleEvent(Event event) {
- handleMouseDoubleClick(event);
- }
- });
- addListener(SWT.MouseMove, new Listener() {
- public void handleEvent(Event event) {
- handleMouseMove(event);
- }
- });
- addListener(SWT.Paint, new Listener() {
- public void handleEvent(Event event) {
- handlePaint(event);
- }
- });
- addListener(SWT.Resize, new Listener() {
- public void handleEvent(Event event) {
- handleResize(event);
- }
- });
- addListener(SWT.Traverse, new Listener() {
- public void handleEvent(Event event) {
- handleTraverse(event);
- }
- });
- if (verticalBar != null) {
- verticalBar.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- handleVerticalScroll(event);
- }
- });
- }
- if (horizontalBar != null) {
- horizontalBar.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- handleHorizontalScroll(event);
- }
- });
- }
-}
-StyledTextContent internalGetContent() {
- return content;
-}
-int internalGetHorizontalPixel() {
- return horizontalScrollOffset;
-}
-LineCache internalGetLineCache() {
- return lineCache;
-}
-Point internalGetSelection() {
- return selection;
-}
-boolean internalGetWordWrap() {
- return wordWrap;
-}
-/**
- * Used by WordWrapCache to bypass StyledText.redraw which does
- * an unwanted cache reset.
- */
-void internalRedraw() {
- super.redraw();
-}
-/**
- * Redraws the specified text range.
- * <p>
- *
- * @param start offset of the first character to redraw
- * @param length number of characters to redraw
- * @param clearBackground true if the background should be cleared as
- * part of the redraw operation. If true, the entire redraw range will
- * be cleared before anything is redrawn. If the redraw range includes
- * the last character of a line (i.e., the entire line is redrawn) the
- * line is cleared all the way to the right border of the widget.
- * The redraw operation will be faster and smoother if clearBackground is
- * set to false. Whether or not the flag can be set to false depends on
- * the type of change that has taken place. If font styles or background
- * colors for the redraw range have changed, clearBackground should be
- * set to true. If only foreground colors have changed for the redraw
- * range, clearBackground can be set to false.
- */
-void internalRedrawRange(int start, int length, boolean clearBackground) {
- int end = start + length;
- int firstLine = content.getLineAtOffset(start);
- int lastLine = content.getLineAtOffset(end);
- int offsetInFirstLine;
- int partialBottomIndex = getPartialBottomIndex();
- int partialTopIndex = verticalScrollOffset / lineHeight;
- // do nothing if redraw range is completely invisible
- if (firstLine > partialBottomIndex || lastLine < partialTopIndex) {
- return;
- }
- // only redraw visible lines
- if (partialTopIndex > firstLine) {
- firstLine = partialTopIndex;
- offsetInFirstLine = 0;
- }
- else {
- offsetInFirstLine = start - content.getOffsetAtLine(firstLine);
- }
- if (partialBottomIndex + 1 < lastLine) {
- lastLine = partialBottomIndex + 1; // + 1 to redraw whole bottom line, including line break
- end = content.getOffsetAtLine(lastLine);
- }
- // redraw first and last lines
- if (isBidi()) {
- redrawBidiLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
- }
- else {
- redrawLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
- }
- // redraw entire center lines if redraw range includes more than two lines
- if (lastLine - firstLine > 1) {
- Rectangle clientArea = getClientArea();
- int redrawStopY = lastLine * lineHeight - verticalScrollOffset;
- int redrawY = (firstLine + 1) * lineHeight - verticalScrollOffset;
- draw(0, redrawY, clientArea.width, redrawStopY - redrawY, clearBackground);
- }
-}
-/**
- * Returns the widget text with style information encoded using RTF format
- * specification version 1.5.
- *
- * @return the widget text with style information encoded using RTF format
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-String getRtf(){
- checkWidget();
- RTFWriter rtfWriter = new RTFWriter(0, getCharCount());
- return getPlatformDelimitedText(rtfWriter);
-}
-/**
- * Frees resources.
- */
-void handleDispose() {
- clipboard.dispose();
- ibeamCursor.dispose();
- if (renderer != null) {
- renderer.dispose();
- renderer = null;
- }
- if (content != null) {
- content.removeTextChangeListener(textChangeListener);
- }
- if (leftCaretBitmap != null) {
- leftCaretBitmap.dispose();
- leftCaretBitmap = null;
- }
- if (rightCaretBitmap != null) {
- rightCaretBitmap.dispose();
- rightCaretBitmap = null;
- }
- if (isBidi()) {
- StyledTextBidi.removeLanguageListener(this);
- }
-}
-/**
- * Scrolls the widget horizontally.
- */
-void handleHorizontalScroll(Event event) {
- int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
- scrollHorizontal(scrollPixel);
-}
-/**
- * If an action has been registered for the key stroke execute the action.
- * Otherwise, if a character has been entered treat it as new content.
- * <p>
- *
- * @param event keyboard event
- */
-void handleKey(Event event) {
- int action;
-
- if (event.keyCode != 0) {
- // special key pressed (e.g., F1)
- action = getKeyBinding(event.keyCode | event.stateMask);
- }
- else {
- // character key pressed
- action = getKeyBinding(event.character | event.stateMask);
- if (action == SWT.NULL) {
- // see if we have a control character
- if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) {
- // get the character from the CTRL+char sequence, the control
- // key subtracts 64 from the value of the key that it modifies
- int c = event.character + 64;
- action = getKeyBinding(c | event.stateMask);
- }
- }
- }
- if (action == SWT.NULL) {
- boolean ignore = false;
-
- if (isCarbon) {
- // Ignore acclerator key combinations (we do not want to
- // insert a character in the text in this instance). Do not
- // ignore COMMAND+ALT combinations since that key sequence
- // produces characters on the mac.
- ignore = (event.stateMask ^ SWT.COMMAND) == 0 ||
- (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0;
- } else {
- // Ignore acclerator key combinations (we do not want to
- // insert a character in the text in this instance). Don't
- // ignore CTRL+ALT combinations since that is the Alt Gr
- // key on some keyboards. See bug 20953.
- ignore = (event.stateMask ^ SWT.ALT) == 0 ||
- (event.stateMask ^ SWT.CTRL) == 0 ||
- (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 ||
- (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
- }
- // -ignore anything below SPACE except for line delimiter keys and tab.
- // -ignore DEL
- if (!ignore && event.character > 31 && event.character != SWT.DEL ||
- event.character == SWT.CR || event.character == SWT.LF ||
- event.character == TAB) {
- doContent(event.character);
- }
- }
- else {
- invokeAction(action);
- }
-}
-/**
- * If a VerifyKey listener exists, verify that the key that was entered
- * should be processed.
- * <p>
- *
- * @param event keyboard event
- */
-void handleKeyDown(Event event) {
- Event verifyEvent = new Event();
-
- verifyEvent.character = event.character;
- verifyEvent.keyCode = event.keyCode;
- verifyEvent.stateMask = event.stateMask;
- verifyEvent.doit = true;
- notifyListeners(VerifyKey, verifyEvent);
- if (verifyEvent.doit == true) {
- handleKey(event);
- }
-}
-/**
- * Updates the caret location and selection if mouse button 1 has been
- * pressed.
- */
-void handleMouseDoubleClick(Event event) {
- if (event.button != 1 || doubleClickEnabled == false) {
- return;
- }
- event.y -= topMargin;
- mouseDoubleClick = true;
- caretOffset = getWordStart(caretOffset);
- resetSelection();
- caretOffset = getWordEndNoSpaces(caretOffset);
- showCaret();
- doMouseSelection();
- doubleClickSelection = new Point(selection.x, selection.y);
-}
-/**
- * Updates the caret location and selection if mouse button 1 has been
- * pressed.
- */
-void handleMouseDown(Event event) {
- boolean select = (event.stateMask & SWT.MOD2) != 0;
-
- if (event.button != 1) {
- return;
- }
- mouseDoubleClick = false;
- event.y -= topMargin;
- doMouseLocationChange(event.x, event.y, select);
-}
-/**
- * Updates the caret location and selection if mouse button 1 is pressed
- * during the mouse move.
- */
-void handleMouseMove(Event event) {
- if ((event.stateMask & SWT.BUTTON1) == 0) {
- return;
- }
- event.y -= topMargin;
- doMouseLocationChange(event.x, event.y, true);
- doAutoScroll(event);
-}
-/**
- * Autoscrolling ends when the mouse button is released.
- */
-void handleMouseUp(Event event) {
- event.y -= topMargin;
- endAutoScroll();
-}
-/**
- * Renders the invalidated area specified in the paint event.
- * <p>
- *
- * @param event paint event
- */
-void handlePaint(Event event) {
- int startLine = Math.max(0, (event.y - topMargin + verticalScrollOffset) / lineHeight);
- int paintYFromTopLine = (startLine - topIndex) * lineHeight;
- int topLineOffset = topIndex * lineHeight - verticalScrollOffset;
- int startY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling and top margin
- int renderHeight = event.y + event.height - startY;
- Rectangle clientArea = getClientArea();
-
- // Check if there is work to do. clientArea.width should never be 0
- // if we receive a paint event but we never want to try and create
- // an Image with 0 width.
- if (clientArea.width == 0 || event.height == 0) {
- return;
- }
- performPaint(event.gc, startLine, startY, renderHeight);
-}
-/**
- * Recalculates the scroll bars. Rewraps all lines when in word
- * wrap mode.
- * <p>
- *
- * @param event resize event
- */
-void handleResize(Event event) {
- int oldHeight = clientAreaHeight;
- int oldWidth = clientAreaWidth;
-
- clientAreaHeight = getClientArea().height;
- clientAreaWidth = getClientArea().width;
- if (wordWrap) {
- if (oldWidth != clientAreaWidth) {
- wordWrapResize(oldWidth);
- }
- }
- else
- if (clientAreaHeight > oldHeight) {
- int lineCount = content.getLineCount();
- int oldBottomIndex = topIndex + oldHeight / lineHeight;
- int newItemCount = Compatibility.ceil(clientAreaHeight - oldHeight, lineHeight);
-
- oldBottomIndex = Math.min(oldBottomIndex, lineCount);
- newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex);
- lineCache.calculate(oldBottomIndex, newItemCount);
- }
- setScrollBars();
- claimBottomFreeSpace();
- claimRightFreeSpace();
- if (oldHeight != clientAreaHeight) {
- calculateTopIndex();
- }
-}
-/**
- * Updates the caret position and selection and the scroll bars to reflect
- * the content change.
- * <p>
- */
-void handleTextChanged(TextChangedEvent event) {
- lineCache.textChanged(lastTextChangeStart,
- lastTextChangeNewLineCount,
- lastTextChangeReplaceLineCount,
- lastTextChangeNewCharCount,
- lastTextChangeReplaceCharCount);
- setScrollBars();
- // update selection/caret location after styles have been changed.
- // otherwise any text measuring could be incorrect
- //
- // also, this needs to be done after all scrolling. Otherwise,
- // selection redraw would be flushed during scroll which is wrong.
- // in some cases new text would be drawn in scroll source area even
- // though the intent is to scroll it.
- // fixes 1GB93QT
- updateSelection(
- lastTextChangeStart,
- lastTextChangeReplaceCharCount,
- lastTextChangeNewCharCount);
-
- if (lastTextChangeReplaceLineCount > 0) {
- // Only check for unused space when lines are deleted.
- // Fixes 1GFL4LY
- // Scroll up so that empty lines below last text line are used.
- // Fixes 1GEYJM0
- claimBottomFreeSpace();
- }
- if (lastTextChangeReplaceCharCount > 0) {
- // fixes bug 8273
- claimRightFreeSpace();
- }
- // do direct drawing if the text change is confined to a single line.
- // optimization and fixes bug 13999. see also handleTextChanging.
- if (lastTextChangeNewLineCount == 0 && lastTextChangeReplaceLineCount == 0) {
- int startLine = content.getLineAtOffset(lastTextChangeStart);
- int startY = startLine * lineHeight - verticalScrollOffset + topMargin;
-
- GC gc = getGC();
- Caret caret = getCaret();
- boolean caretVisible = false;
-
- if (caret != null) {
- caretVisible = caret.getVisible();
- caret.setVisible(false);
- }
- performPaint(gc, startLine, startY, lineHeight);
- if (caret != null) {
- caret.setVisible(caretVisible);
- }
- gc.dispose();
- }
-}
-/**
- * Updates the screen to reflect a pending content change.
- * <p>
- *
- * @param event.start the start offset of the change
- * @param event.newText text that is going to be inserted or empty String
- * if no text will be inserted
- * @param event.replaceCharCount length of text that is going to be replaced
- * @param event.newCharCount length of text that is going to be inserted
- * @param event.replaceLineCount number of lines that are going to be replaced
- * @param event.newLineCount number of new lines that are going to be inserted
- */
-void handleTextChanging(TextChangingEvent event) {
- int firstLine;
- int textChangeY;
- boolean isMultiLineChange = event.replaceLineCount > 0 || event.newLineCount > 0;
-
- if (event.replaceCharCount < 0) {
- event.start += event.replaceCharCount;
- event.replaceCharCount *= -1;
- }
- lastTextChangeStart = event.start;
- lastTextChangeNewLineCount = event.newLineCount;
- lastTextChangeNewCharCount = event.newCharCount;
- lastTextChangeReplaceLineCount = event.replaceLineCount;
- lastTextChangeReplaceCharCount = event.replaceCharCount;
- firstLine = content.getLineAtOffset(event.start);
- textChangeY = firstLine * lineHeight - verticalScrollOffset + topMargin;
- if (isMultiLineChange) {
- redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount);
- }
- // notify default line styler about text change
- if (defaultLineStyler != null) {
- defaultLineStyler.textChanging(event);
- }
-
- // Update the caret offset if it is greater than the length of the content.
- // This is necessary since style range API may be called between the
- // handleTextChanging and handleTextChanged events and this API sets the
- // caretOffset.
- int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
- if (caretOffset > newEndOfText) caretOffset = newEndOfText;
-}
-/**
- * Called when the widget content is set programatically, overwriting
- * the old content. Resets the caret position, selection and scroll offsets.
- * Recalculates the content width and scroll bars. Redraws the widget.
- * <p>
- *
- * @param event text change event.
- */
-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
- * traversal and receive the tab key event instead.
- * <p>
- *
- * @param event the event
- */
-void handleTraverse(Event event) {
- int style = getStyle();
- boolean ignoreTab = (style & SWT.MULTI) != 0 && !editable || isSingleLine();
-
- if ((event.detail == SWT.TRAVERSE_TAB_NEXT ||
- event.detail == SWT.TRAVERSE_RETURN) && ignoreTab) {
- event.doit = true;
- }
-}
-/**
- * Scrolls the widget vertically.
- */
-void handleVerticalScroll(Event event) {
- setVerticalScrollOffset(getVerticalBar().getSelection(), false);
-}
-/**
- * Initializes the fonts used to render font styles.
- * Presently only regular and bold fonts are supported.
- */
-void initializeRenderer() {
- if (renderer != null) {
- renderer.dispose();
- }
- renderer = new DisplayRenderer(
- getDisplay(), getFont(), isBidi(), leftMargin, this, tabLength);
- lineHeight = renderer.getLineHeight();
- lineEndSpaceWidth = renderer.getLineEndSpaceWidth();
-}
-/**
- * Executes the action.
- * <p>
- *
- * @param action one of the actions defined in ST.java
- */
-public void invokeAction(int action) {
- int oldColumnX;
- int caretLine;
-
- checkWidget();
- switch (action) {
- // Navigation
- case ST.LINE_UP:
- caretLine = doLineUp();
- oldColumnX = columnX;
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- // save the original horizontal caret position
- columnX = oldColumnX;
- clearSelection(true);
- break;
- case ST.LINE_DOWN:
- caretLine = doLineDown();
- oldColumnX = columnX;
- // explicitly go to the calculated caret line. may be different
- // from content.getLineAtOffset(caretOffset) when in word wrap mode
- showCaret(caretLine);
- // save the original horizontal caret position
- columnX = oldColumnX;
- clearSelection(true);
- break;
- case ST.LINE_START:
- doLineStart();
- clearSelection(true);
- break;
- case ST.LINE_END:
- doLineEnd();
- clearSelection(true);
- break;
- case ST.COLUMN_PREVIOUS:
- doCursorPrevious();
- clearSelection(true);
- break;
- case ST.COLUMN_NEXT:
- doCursorNext();
- clearSelection(true);
- break;
- case ST.PAGE_UP:
- doPageUp();
- clearSelection(true);
- break;
- case ST.PAGE_DOWN:
- doPageDown(false);
- clearSelection(true);
- break;
- case ST.WORD_PREVIOUS:
- doWordPrevious();
- clearSelection(true);
- break;
- case ST.WORD_NEXT:
- doWordNext();
- clearSelection(true);
- break;
- case ST.TEXT_START:
- doContentStart();
- clearSelection(true);
- break;
- case ST.TEXT_END:
- doContentEnd();
- clearSelection(true);
- break;
- case ST.WINDOW_START:
- doPageStart();
- clearSelection(true);
- break;
- case ST.WINDOW_END:
- doPageEnd();
- clearSelection(true);
- break;
- // Selection
- case ST.SELECT_LINE_UP:
- doSelectionLineUp();
- break;
- case ST.SELECT_LINE_DOWN:
- doSelectionLineDown();
- break;
- case ST.SELECT_LINE_START:
- doLineStart();
- doSelection(SWT.LEFT);
- break;
- case ST.SELECT_LINE_END:
- doLineEnd();
- doSelection(SWT.RIGHT);
- break;
- case ST.SELECT_COLUMN_PREVIOUS:
- doSelectionCursorPrevious();
- doSelection(SWT.LEFT);
- break;
- case ST.SELECT_COLUMN_NEXT:
- doSelectionCursorNext();
- doSelection(SWT.RIGHT);
- break;
- case ST.SELECT_PAGE_UP:
- doSelectionPageUp();
- doSelection(SWT.LEFT);
- break;
- case ST.SELECT_PAGE_DOWN:
- doSelectionPageDown();
- break;
- case ST.SELECT_WORD_PREVIOUS:
- doSelectionWordPrevious();
- doSelection(SWT.LEFT);
- break;
- case ST.SELECT_WORD_NEXT:
- doSelectionWordNext();
- doSelection(SWT.RIGHT);
- break;
- case ST.SELECT_TEXT_START:
- doContentStart();
- doSelection(SWT.LEFT);
- break;
- case ST.SELECT_TEXT_END:
- doContentEnd();
- doSelection(SWT.RIGHT);
- break;
- case ST.SELECT_WINDOW_START:
- doPageStart();
- doSelection(SWT.LEFT);
- break;
- case ST.SELECT_WINDOW_END:
- doPageEnd();
- doSelection(SWT.RIGHT);
- break;
- // Modification
- case ST.CUT:
- cut();
- break;
- case ST.COPY:
- copy();
- break;
- case ST.PASTE:
- paste();
- break;
- case ST.DELETE_PREVIOUS:
- doBackspace();
- break;
- case ST.DELETE_NEXT:
- doDelete();
- break;
- case ST.DELETE_WORD_PREVIOUS:
- doDeleteWordPrevious();
- break;
- case ST.DELETE_WORD_NEXT:
- doDeleteWordNext();
- break;
- // Miscellaneous
- case ST.TOGGLE_OVERWRITE:
- overwrite = !overwrite; // toggle insert/overwrite mode
- break;
- }
-}
-/**
- * Temporary until SWT provides this
- */
-boolean isBidi() {
- return isBidi;
-}
-/**
- * Returns whether the given offset is inside a multi byte line delimiter.
- * Example:
- * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
- *
- * @return true if the given offset is inside a multi byte line delimiter.
- * false if the given offset is before or after a line delimiter.
- */
-boolean isLineDelimiter(int offset) {
- int line = content.getLineAtOffset(offset);
- int lineOffset = content.getOffsetAtLine(line);
- int offsetInLine = offset - lineOffset;
- // offsetInLine will be greater than line length if the line
- // delimiter is longer than one character and the offset is set
- // in between parts of the line delimiter.
- return offsetInLine > content.getLine(line).length();
-}
-/**
- * Returns whether or not the given lines are visible.
- * <p>
- *
- * @return true if any of the lines is visible
- * false if none of the lines is visible
- */
-boolean isAreaVisible(int firstLine, int lastLine) {
- int partialBottomIndex = getPartialBottomIndex();
- int partialTopIndex = verticalScrollOffset / lineHeight;
- boolean notVisible = firstLine > partialBottomIndex || lastLine < partialTopIndex;
- return !notVisible;
-}
-/**
- * Returns whether or not the given styles will necessitate a redraw for the given start line.
- * A redraw is necessary when font style changes after the start of a style will take place.
- * This method assumes ranges is in order and non-overlapping.
- * <p>
- *
- * @return true if a redraw of the given line is necessary, false otherwise
- */
-boolean isRedrawFirstLine(StyleRange[] ranges, int firstLine, int firstLineOffset) {
- int lineEnd = firstLineOffset + content.getLine(firstLine).length();
- for (int i=0; i<ranges.length; i++) {
- StyleRange range = ranges[i];
- // does the style start on the first line?
- if (range.start < lineEnd) {
- int rangeEnd = range.start + range.length;
- if (isStyleChanging(range, range.start, Math.min(rangeEnd, lineEnd))) return true;
- } else {
- return false;
- }
- }
- return false;
-}
-/**
- * Returns whether or not the given styles will necessitate a redraw for the given end line.
- * A redraw is necessary when font style changes after the start of a style will take place.
- * This method assumes ranges is in order and non-overlapping.
- * <p>
- *
- * @return true if a redraw of the last line is necessary, false otherwise
- */
-boolean isRedrawLastLine(StyleRange[] ranges, int lastLine, int lastLineOffset) {
- for (int i = ranges.length - 1; i >= 0; i--) {
- StyleRange range = ranges[i];
- int rangeEnd = range.start + range.length;
- // does style range end on the last line?
- if (rangeEnd >= lastLineOffset) {
- if (isStyleChanging(range, Math.max(range.start, lastLineOffset), rangeEnd)) return true;
- } else {
- break;
- }
- }
- return false;
-}
-/**
- * Returns whether the widget can have only one line.
- * <p>
- *
- * @return true if widget can have only one line, false if widget can have
- * multiple lines
- */
-boolean isSingleLine() {
- return (getStyle() & SWT.SINGLE) != 0;
-}
-/**
- * Returns whether the font style in the given style range is changing
- * from SWT.NORMAL to SWT.BOLD or vice versa.
- * <p>
- *
- * @param range StyleRange to compare current font style with.
- * @param start offset of the first font style to compare
- * @param end offset behind the last font style to compare
- * @return true if the font style is changing in the given style range,
- * false if the font style is not changing in the given style range.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-boolean isStyleChanging(StyleRange range, int start, int end) {
- checkWidget();
- StyleRange[] styles = defaultLineStyler.getStyleRangesFor(start, end - start);
-
- if (styles == null) {
- return (range.fontStyle != SWT.NORMAL);
- }
- for (int i = 0; i < styles.length; i++) {
- StyleRange newStyle = styles[i];
- if (newStyle.fontStyle != range.fontStyle) {
- return true;
- }
- }
- return false;
-}
-/**
- * Sends the specified verify event, replace/insert text as defined by
- * the event and send a modify event.
- * <p>
- *
- * @param event the text change event.
- * <ul>
- * <li>event.start - the replace start offset</li>
- * <li>event.end - the replace end offset</li>
- * <li>event.text - the new text</li>
- * </ul>
- * @param updateCaret whether or not he caret should be set behind
- * the new text
- */
-void modifyContent(Event event, boolean updateCaret) {
- event.doit = true;
- notifyListeners(SWT.Verify, event);
- if (event.doit) {
- StyledTextEvent styledTextEvent = null;
- int replacedLength = event.end - event.start;
- boolean isCharacterRemove = replacedLength == 1 && event.text.length() == 0;
- boolean isBackspace = event.start < caretOffset;
- boolean isDirectionBoundary = false;
-
- if (updateCaret && isBidi() && isCharacterRemove) {
- // set the keyboard language to the language of the deleted character.
- // determine direction boundary so that caret location can be updated
- // properly.
- int line = content.getLineAtOffset(caretOffset);
- int lineStartOffset = content.getOffsetAtLine(line);
- int offsetInLine = caretOffset - lineStartOffset;
- String lineText = content.getLine(line);
- GC gc = getGC();
- StyledTextBidi bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
- if (isBackspace) {
- if (offsetInLine > 0) {
- // the line start/end does not represent a direction boundary
- // even if the previous/next line has a different direction.
- isDirectionBoundary =
- offsetInLine < lineText.length() &&
- (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) ||
- bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
- bidi.setKeyboardLanguage(offsetInLine - 1);
- }
- }
- else {
- if (offsetInLine < lineText.length()) {
- // the line start/end does not represent a direction boundary
- // even if the previous/next line has a different direction.
- isDirectionBoundary =
- offsetInLine > 0 &&
- (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) ||
- bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
- bidi.setKeyboardLanguage(offsetInLine);
- }
- }
- gc.dispose();
- }
- if (isListening(ExtendedModify)) {
- styledTextEvent = new StyledTextEvent(logicalContent);
- styledTextEvent.start = event.start;
- styledTextEvent.end = event.start + event.text.length();
- styledTextEvent.text = content.getTextRange(event.start, replacedLength);
- }
- content.replaceTextRange(event.start, replacedLength, event.text);
- // set the caret position prior to sending the modify event.
- // fixes 1GBB8NJ
- if (updateCaret) {
- // always update the caret location. fixes 1G8FODP
- internalSetSelection(event.start + event.text.length(), 0, true);
- if (isBidi()) {
- // Update the caret direction so that the caret moves to the
- // typed/deleted character. Fixes 1GJLQ16.
- if (isCharacterRemove) {
- updateBidiDirection(isBackspace, isDirectionBoundary);
- }
- else {
- lastCaretDirection = ST.COLUMN_NEXT;
- }
- showBidiCaret();
- }
- else {
- showCaret();
- }
- }
- notifyListeners(SWT.Modify, event);
- if (isListening(ExtendedModify)) {
- notifyListeners(ExtendedModify, styledTextEvent);
- }
- }
-}
-/**
- * Replaces the selection with the clipboard text or insert the text at
- * the current caret offset if there is no selection.
- * If the widget has the SWT.SINGLE style and the clipboard text contains
- * more than one line, only the first line without line delimiters is
- * inserted in the widget.
- * <p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void paste(){
- checkWidget();
- TextTransfer transfer = TextTransfer.getInstance();
- String text;
- text = (String) clipboard.getContents(transfer);
- if (text != null && text.length() > 0) {
- Event event = new Event();
- event.start = selection.x;
- event.end = selection.y;
- event.text = getModelDelimitedText(text);
- sendKeyEvent(event);
- }
-}
-/**
- * Render the specified area. Broken out as its own method to support
- * direct drawing.
- * <p>
- *
- * @param gc GC to render on
- * @param startLine first line to render
- * @param startY y pixel location to start rendering at
- * @param renderHeight renderHeight widget area that needs to be filled with lines
- */
-void performPaint(GC gc,int startLine,int startY, int renderHeight) {
- Rectangle clientArea = getClientArea();
- Color background = getBackground();
-
- // Check if there is work to do. We never want to try and create
- // an Image with 0 width or 0 height.
- if (clientArea.width == 0) {
- return;
- }
- if (renderHeight > 0) {
- // renderHeight will be negative when only top margin needs redrawing
- Color foreground = getForeground();
- int lineCount = content.getLineCount();
- int paintY = 0;
-
- if (isSingleLine()) {
- lineCount = 1;
- if (startLine > 1) {
- startLine = 1;
- }
- }
- Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight);
- GC lineGC = new GC(lineBuffer);
-
- lineGC.setFont(getFont());
- renderer.setCurrentFontStyle(SWT.NORMAL);
- lineGC.setForeground(foreground);
- lineGC.setBackground(background);
-
- for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) {
- String line = content.getLine(i);
- renderer.drawLine(line, i, paintY, lineGC, background, foreground, true);
- }
- if (paintY < renderHeight) {
- lineGC.setBackground(background);
- lineGC.setForeground(background);
- lineGC.fillRectangle(0, paintY, clientArea.width, renderHeight - paintY);
- }
- gc.drawImage(lineBuffer, 0, startY);
- lineGC.dispose();
- lineBuffer.dispose();
- }
- clearMargin(gc, background, clientArea, renderHeight);
-}
-/**
- * Prints the widget's text to the default printer.
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void print() {
- checkWidget();
- Printer printer = new Printer();
- StyledTextPrintOptions options = new StyledTextPrintOptions();
-
- options.printTextForeground = true;
- options.printTextBackground = true;
- options.printTextFontStyle = true;
- options.printLineBackground = true;
- new Printing(this, printer, options).run();
- printer.dispose();
-}
-/**
- * Returns a runnable that will print the widget's text
- * to the specified printer.
- * <p>
- * The runnable may be run in a non-UI thread.
- * </p>
- *
- * @param printer the printer to print to
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <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 printer is null</li>
- * </ul>
- */
-public Runnable print(Printer printer) {
- StyledTextPrintOptions options = new StyledTextPrintOptions();
-
- checkWidget();
- options.printTextForeground = true;
- options.printTextBackground = true;
- options.printTextFontStyle = true;
- options.printLineBackground = true;
- if (printer == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- return print(printer, options);
-}
-/**
- * Returns a runnable that will print the widget's text
- * to the specified printer.
- * <p>
- * The runnable may be run in a non-UI thread.
- * </p>
- *
- * @param printer the printer to print to
- * @param options print options to use during printing
- * @exception SWTException <ul>
- * <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 printer or options is null</li>
- * </ul>
- * @since 2.1
- */
-public Runnable print(Printer printer, StyledTextPrintOptions options) {
- checkWidget();
- if (printer == null || options == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- return new Printing(this, printer, options);
-}
-/**
- * Causes the entire bounds of the receiver to be marked
- * as needing to be redrawn. The next time a paint request
- * 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
- * and that the content width may have changed.
- * </p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @see Control#update
- */
-public void redraw() {
- int itemCount;
-
- super.redraw();
- itemCount = getPartialBottomIndex() - topIndex + 1;
- lineCache.redrawReset(topIndex, itemCount, true);
- lineCache.calculate(topIndex, itemCount);
- setHorizontalScrollBar();
-}
-/**
- * Causes the rectangular area of the receiver specified by
- * the arguments to be marked as needing to be redrawn.
- * The 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
- * <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
- * and that the content width may have changed.
- * </p>
- *
- * @param x the x coordinate of the area to draw
- * @param y the y coordinate of the area to draw
- * @param width the width of the area to draw
- * @param height the height of the area to draw
- * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @see Control#update
- */
-public void redraw(int x, int y, int width, int height, boolean all) {
- super.redraw(x, y, width, height, all);
- if (height > 0) {
- int lineCount = content.getLineCount();
- int startLine = (getTopPixel() + y) / lineHeight;
- int endLine = startLine + Compatibility.ceil(height, lineHeight);
- int itemCount;
-
- // reset all lines in the redraw rectangle
- startLine = Math.min(startLine, lineCount);
- itemCount = Math.min(endLine, lineCount) - startLine;
- lineCache.reset(startLine, itemCount, true);
- // only calculate the visible lines
- itemCount = getPartialBottomIndex() - topIndex + 1;
- lineCache.calculate(topIndex, itemCount);
- setHorizontalScrollBar();
- }
-}
-/**
- * Redraws a text range in the specified lines
- * <p>
- *
- * @param firstLine first line to redraw at the specified offset
- * @param offsetInFirstLine offset in firstLine to start redrawing
- * @param lastLine last line to redraw
- * @param endOffset offset in the last where redrawing should stop
- * @param clearBackground true=clear the background by invalidating
- * the requested redraw range, false=draw the foreground directly
- * without invalidating the redraw range.
- */
-void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
- int lineCount = lastLine - firstLine + 1;
- int redrawY = firstLine * lineHeight - verticalScrollOffset;
- int firstLineOffset = content.getOffsetAtLine(firstLine);
- String line = content.getLine(firstLine);
- GC gc = getGC();
- StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc);
-
- bidi.redrawRange(
- this, offsetInFirstLine,
- Math.min(line.length(), endOffset) - offsetInFirstLine,
- leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight);
- // redraw line break marker (either space or full client area width)
- // if redraw range extends over more than one line and background should be redrawn
- if (lastLine > firstLine && clearBackground) {
- int lineBreakWidth;
- int lineBreakStartX = bidi.getTextWidth();
- // handle empty line case
- if (lineBreakStartX == leftMargin) {
- lineBreakStartX += XINSET;
- }
- lineBreakStartX = lineBreakStartX - horizontalScrollOffset;
- if ((getStyle() & SWT.FULL_SELECTION) != 0) {
- lineBreakWidth = getClientArea().width - lineBreakStartX;
- }
- else {
- lineBreakWidth = lineEndSpaceWidth;
- }
- draw(lineBreakStartX, redrawY, lineBreakWidth, lineHeight, clearBackground);
- }
- // redraw last line if more than one line needs redrawing
- if (lineCount > 1) {
- int lastLineOffset = content.getOffsetAtLine(lastLine);
- int offsetInLastLine = endOffset - lastLineOffset;
- // no redraw necessary if redraw offset is 0
- if (offsetInLastLine > 0) {
- line = content.getLine(lastLine);
- redrawY = lastLine * lineHeight - verticalScrollOffset;
- bidi = getStyledTextBidi(line, lastLineOffset, gc);
- bidi.redrawRange(
- this, 0, offsetInLastLine,
- leftMargin - horizontalScrollOffset,
- redrawY + topMargin, lineHeight);
- }
- }
- gc.dispose();
-}
-/**
- * Redraw the given line.
- * <p>
- *
- * @param line index of the line to redraw
- * @param offset offset in line to start redrawing
- */
-void redrawLine(int line, int offset) {
- int redrawX = 0;
- if (offset > 0) {
- String lineText = content.getLine(line);
- redrawX = getXAtOffset(lineText, line, offset);
- }
- int redrawY = line * lineHeight - verticalScrollOffset;
- super.redraw(
- redrawX + leftMargin, redrawY + topMargin,
- getClientArea().width, lineHeight, true);
-}
-/**
- * Redraws a text range in the specified lines
- * <p>
- *
- * @param firstLine first line to redraw at the specified offset
- * @param offsetInFirstLine offset in firstLine to start redrawing
- * @param lastLine last line to redraw
- * @param endOffset offset in the last where redrawing should stop
- * @param clearBackground true=clear the background by invalidating
- * the requested redraw range. If the redraw range includes the
- * last character of a line (i.e., the entire line is redrawn) the
- * line is cleared all the way to the right border of the widget.
- * false=draw the foreground directly without invalidating the
- * redraw range.
- */
-void redrawLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
- String line = content.getLine(firstLine);
- int lineCount = lastLine - firstLine + 1;
- int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine) - leftMargin;
- int redrawStopX;
- int redrawY = firstLine * lineHeight - verticalScrollOffset;
- int firstLineOffset = content.getOffsetAtLine(firstLine);
- boolean fullLineRedraw = ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine);
-
- // if redraw range includes last character on the first line,
- // clear background to right widget border. fixes bug 19595.
- if (clearBackground && endOffset - firstLineOffset >= line.length()) {
- fullLineRedraw = true;
- }
- // calculate redraw stop location
- if (fullLineRedraw) {
- redrawStopX = getClientArea().width - leftMargin;
- }
- else {
- redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset) - leftMargin;
- }
- draw(redrawX, redrawY, redrawStopX - redrawX, lineHeight, clearBackground);
- // redraw last line if more than one line needs redrawing
- if (lineCount > 1) {
- int offsetInLastLine = endOffset - content.getOffsetAtLine(lastLine);
- // no redraw necessary if redraw offset is 0
- if (offsetInLastLine > 0) {
- line = content.getLine(lastLine);
- // if redraw range includes last character on the last line,
- // clear background to right widget border. fixes bug 19595.
- if (clearBackground && offsetInLastLine >= line.length()) {
- fullLineRedraw = true;
- }
- if (fullLineRedraw) {
- redrawStopX = getClientArea().width - leftMargin;
- }
- else {
- redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine) - leftMargin;
- }
- redrawY = lastLine * lineHeight - verticalScrollOffset;
- draw(0, redrawY, redrawStopX, lineHeight, clearBackground);
- }
- }
-}
-/**
- * Fixes the widget to display a text change.
- * Bit blitting and redrawing is done as necessary.
- * <p>
- *
- * @param y y location of the text change
- * @param newLineCount number of new lines.
- * @param replacedLineCount number of replaced lines.
- */
-void redrawMultiLineChange(int y, int newLineCount, int replacedLineCount) {
- Rectangle clientArea = getClientArea();
- int lineCount = newLineCount - replacedLineCount;
- int sourceY;
- int destinationY;
-
- if (lineCount > 0) {
- sourceY = Math.max(0, y + lineHeight);
- destinationY = sourceY + lineCount * lineHeight;
- }
- else {
- destinationY = Math.max(0, y + lineHeight);
- sourceY = destinationY - lineCount * lineHeight;
- }
- scroll(
- 0, destinationY, // destination x, y
- 0, sourceY, // source x, y
- clientArea.width, clientArea.height, true);
- // Always redrawing causes the bottom line to flash when a line is
- // deleted. This is because SWT merges the paint area of the scroll
- // with the paint area of the redraw call below.
- // To prevent this we could call update after the scroll. However,
- // adding update can cause even more flash if the client does other
- // redraw/update calls (ie. for syntax highlighting).
- // We could also redraw only when a line has been added or when
- // contents has been added to a line. This would require getting
- // line index info from the content and is not worth the trouble
- // (the flash is only on the bottom line and minor).
- // Specifying the NO_MERGE_PAINTS style bit prevents the merged
- // redraw but could cause flash/slowness elsewhere.
- if (y + lineHeight > 0 && y <= clientArea.height) {
- // redraw first changed line in case a line was split/joined
- super.redraw(0, y, clientArea.width, lineHeight, true);
- }
- if (newLineCount > 0) {
- int redrawStartY = y + lineHeight;
- int redrawHeight = newLineCount * lineHeight;
-
- if (redrawStartY + redrawHeight > 0 && redrawStartY <= clientArea.height) {
- // display new text
- super.redraw(0, redrawStartY, clientArea.width, redrawHeight, true);
- }
- }
-}
-/**
- * Redraws the specified text range.
- * <p>
- *
- * @param start offset of the first character to redraw
- * @param length number of characters to redraw
- * @param clearBackground true if the background should be cleared as
- * part of the redraw operation. If true, the entire redraw range will
- * be cleared before anything is redrawn. If the redraw range includes
- * the last character of a line (i.e., the entire line is redrawn) the
- * line is cleared all the way to the right border of the widget.
- * The redraw operation will be faster and smoother if clearBackground
- * is set to false. Whether or not the flag can be set to false depends
- * on the type of change that has taken place. If font styles or
- * background colors for the redraw range have changed, clearBackground
- * should be set to true. If only foreground colors have changed for
- * the redraw range, clearBackground can be set to false.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
- * </ul>
- */
-public void redrawRange(int start, int length, boolean clearBackground) {
- checkWidget();
- int end = start + length;
- int contentLength = content.getCharCount();
- int firstLine;
- int lastLine;
-
- if (start > end || start < 0 || end > contentLength) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- firstLine = content.getLineAtOffset(start);
- lastLine = content.getLineAtOffset(end);
- // reset all affected lines but let the redraw recalculate only
- // those that are visible.
- lineCache.reset(firstLine, lastLine - firstLine + 1, true);
- internalRedrawRange(start, length, clearBackground);
-}
-/**
- * Removes the specified bidirectional segment listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- * @since 2.0
- */
-public void removeBidiSegmentListener(BidiSegmentListener listener) {
- checkWidget();
- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- removeListener(LineGetSegments, listener);
-}
-/**
- * Removes the specified extended modify listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
- checkWidget();
- if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- removeListener(ExtendedModify, extendedModifyListener);
-}
-/**
- * Removes the specified line background listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeLineBackgroundListener(LineBackgroundListener listener) {
- checkWidget();
- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- removeListener(LineGetBackground, listener);
- // use default line styler if last user line styler was removed.
- if (isListening(LineGetBackground) == false && userLineBackground) {
- StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
- addListener(LineGetBackground, typedListener);
- userLineBackground = false;
- }
-}
-/**
- * Removes the specified line style listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeLineStyleListener(LineStyleListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- removeListener(LineGetStyle, listener);
- // use default line styler if last user line styler was removed. Fixes 1G7B1X2
- if (isListening(LineGetStyle) == false && userLineStyle) {
- StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
- addListener(LineGetStyle, typedListener);
- userLineStyle = false;
- }
-}
-/**
- * Removes the specified modify listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeModifyListener(ModifyListener modifyListener) {
- checkWidget();
- if (modifyListener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- removeListener(SWT.Modify, modifyListener);
-}
-/**
- * Removes the specified selection listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- removeListener(SWT.Selection, listener);
-}
-/**
- * Removes the specified verify listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeVerifyListener(VerifyListener verifyListener) {
- checkWidget();
- if (verifyListener == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- removeListener(SWT.Verify, verifyListener);
-}
-/**
- * Removes the specified key verify listener.
- * <p>
- *
- * @param listener the listener
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeVerifyKeyListener(VerifyKeyListener listener) {
- if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
- removeListener(VerifyKey, listener);
-}
-/**
- * Replaces the styles in the given range with new styles. This method
- * effectively deletes the styles in the given range and then adds the
- * the new styles.
- * <p>
- * 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
- * and length. The style rendering is undefined if the ranges do overlap
- * or are ill-defined. Must not be null.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
- * <li>ERROR_NULL_ARGUMENT when string is null</li>
- * </ul>
- * @since 2.0
- */
-public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
- checkWidget();
- if (userLineStyle) {
- return;
- }
- if (ranges == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- if (ranges.length == 0) {
- setStyleRange(new StyleRange(start, length, null, null));
- return;
- }
- int end = start + length;
- if (start > end || start < 0 || end > getCharCount()) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
-
- int firstLine = content.getLineAtOffset(start);
- int lastLine = content.getLineAtOffset(end);
-
- // if the area is not visible, there is no need to redraw
- boolean redrawLines = isAreaVisible(firstLine, lastLine);
-
- if (!redrawLines) {
- defaultLineStyler.replaceStyleRanges(start, length, ranges);
- lineCache.reset(firstLine, lastLine - firstLine + 1, true);
- } else {
- boolean redrawFirstLine = false;
- boolean redrawLastLine = false;
- // the first and last line needs to be redrawn completely if the
- // font style is changing from SWT.NORMAL to something else or
- // vice versa. fixes 1G7M5WE.
- int firstLineOffset = content.getOffsetAtLine(firstLine);
- if (isBidi()) {
- redrawFirstLine = true;
- redrawLastLine = true;
- } else {
- int firstLineEnd = firstLineOffset + content.getLine(firstLine).length();
- redrawFirstLine = isRedrawFirstLine(ranges, firstLine, firstLineOffset);
- // check if any bold styles will be cleared
- StyleRange clearRange = new StyleRange(firstLineOffset, firstLineEnd - firstLineOffset, null, null);
- redrawFirstLine = redrawFirstLine || isRedrawFirstLine(new StyleRange[] {clearRange}, firstLine, firstLineOffset);
- if (lastLine != firstLine) {
- int lastLineOffset = content.getOffsetAtLine(lastLine);
- int lastLineEnd = lastLineOffset + content.getLine(lastLine).length();
- redrawLastLine = isRedrawLastLine(ranges, lastLine, lastLineOffset);
- // check if any bold styles will be cleared
- clearRange = new StyleRange(lastLineOffset, lastLineEnd - lastLineOffset, null, null);
- redrawLastLine = redrawLastLine || isRedrawLastLine(new StyleRange[] {clearRange}, lastLine, lastLineOffset);
- }
- }
- defaultLineStyler.replaceStyleRanges(start, length, ranges);
- // reset all lines affected by the style change but let the redraw
- // recalculate only those that are visible.
- lineCache.reset(firstLine, lastLine - firstLine + 1, true);
- internalRedrawRange(start, length, true);
- if (redrawFirstLine) {
- redrawLine(firstLine, start - firstLineOffset);
- }
- if (redrawLastLine) {
- redrawLine(lastLine, 0);
- }
- }
-
- // make sure that the caret is positioned correctly.
- // caret location may change if font style changes.
- // fixes 1G8FODP
- setCaretLocation();
-}
-/**
- * Replaces the given text range with new text.
- * If the widget has the SWT.SINGLE style and "text" contains more than
- * one line, only the first line is rendered but the text is stored
- * unchanged. A subsequent call to getText will return the same text
- * that was set. Note that only a single line of text should be set when
- * the SWT.SINGLE style is used.
- * <p>
- * <b>NOTE:</b> During the replace operation the current selection is
- * changed as follows:
- * <ul>
- * <li>selection before replaced text: selection unchanged
- * <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
- * </ul>
- * </p>
- *
- * @param start offset of first character to replace
- * @param length number of characters to replace. Use 0 to insert text
- * @param text new text. May be empty to delete text.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
- * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
- * Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li>
- * <li>ERROR_NULL_ARGUMENT when string is null</li>
- * </ul>
- */
-public void replaceTextRange(int start, int length, String text) {
- checkWidget();
- int contentLength = getCharCount();
- int end = start + length;
- Event event = new Event();
-
- if (start > end || start < 0 || end > contentLength) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- if (text == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- event.start = start;
- event.end = end;
- event.text = text;
- modifyContent(event, false);
-}
-/**
- * Resets the caret position, selection and scroll offsets. Recalculate
- * the content width and scroll bars. Redraw the widget.
- */
-void reset() {
- ScrollBar verticalBar = getVerticalBar();
- ScrollBar horizontalBar = getHorizontalBar();
- caretOffset = 0;
- topIndex = 0;
- topOffset = 0;
- verticalScrollOffset = 0;
- horizontalScrollOffset = 0;
- resetSelection();
- // discard any styles that may have been set by creating a
- // new default line styler
- if (defaultLineStyler != null) {
- removeLineBackgroundListener(defaultLineStyler);
- removeLineStyleListener(defaultLineStyler);
- installDefaultLineStyler();
- }
- calculateContentWidth();
- if (verticalBar != null) {
- verticalBar.setSelection(0);
- }
- if (horizontalBar != null) {
- horizontalBar.setSelection(0);
- }
- setScrollBars();
- setCaretLocation();
- super.redraw();
-}
-/**
- * Resets the selection.
- */
-void resetSelection() {
- selection.x = selection.y = caretOffset;
- selectionAnchor = -1;
-}
-/**
- * Scrolls the widget horizontally.
- * <p>
- *
- * @param pixels number of pixels to scroll, > 0 = scroll left,
- * < 0 scroll right
- */
-void scrollHorizontal(int pixels) {
- Rectangle clientArea;
-
- if (pixels == 0) {
- return;
- }
- clientArea = getClientArea();
- if (pixels > 0) {
- int sourceX = leftMargin + pixels;
- int scrollWidth = clientArea.width - sourceX - rightMargin;
- int scrollHeight = clientArea.height - topMargin - bottomMargin;
- scroll(
- leftMargin, topMargin, // destination x, y
- sourceX, topMargin, // source x, y
- scrollWidth, scrollHeight, true);
- if (sourceX > scrollWidth) {
- // redraw from end of scrolled area to beginning of scroll
- // invalidated area
- super.redraw(
- leftMargin + scrollWidth, topMargin,
- pixels - scrollWidth, scrollHeight, true);
- }
- }
- else {
- int destinationX = leftMargin - pixels;
- int scrollWidth = clientArea.width - destinationX - rightMargin;
- int scrollHeight = clientArea.height - topMargin - bottomMargin;
- scroll(
- destinationX, topMargin, // destination x, y
- leftMargin, topMargin, // source x, y
- scrollWidth, scrollHeight, true);
- if (destinationX > scrollWidth) {
- // redraw from end of scroll invalidated area to scroll
- // destination
- super.redraw(
- leftMargin + scrollWidth, topMargin,
- -pixels - scrollWidth, scrollHeight, true);
- }
- }
- horizontalScrollOffset += pixels;
- setCaretLocation();
-}
-/**
- * Scrolls the widget horizontally and adjust the horizontal scroll
- * bar to reflect the new horizontal offset..
- * <p>
- *
- * @param pixels number of pixels to scroll, > 0 = scroll left,
- * < 0 scroll right
- * @return
- * true=the widget was scrolled
- * false=the widget was not scrolled, the given offset is not valid.
- */
-boolean scrollHorizontalBar(int pixels) {
- if (pixels == 0) {
- return false;
- }
- ScrollBar horizontalBar = getHorizontalBar();
- if (horizontalBar != null) {
- horizontalBar.setSelection(horizontalScrollOffset + pixels);
- }
- scrollHorizontal(pixels);
- return true;
-}
-/**
- * Selects all the text.
- * <p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void selectAll() {
- checkWidget();
- setSelection(new Point(0, Math.max(getCharCount(),0)));
-}
-/**
- * Replaces/inserts text as defined by the event.
- * <p>
- *
- * @param event the text change event.
- * <ul>
- * <li>event.start - the replace start offset</li>
- * <li>event.end - the replace end offset</li>
- * <li>event.text - the new text</li>
- * </ul>
- */
-void sendKeyEvent(Event event) {
- if (editable == false) {
- return;
- }
- modifyContent(event, true);
-}
-/**
- * Sends the specified selection event.
- */
-void sendSelectionEvent() {
- Event event = new Event();
- event.x = selection.x;
- event.y = selection.y;
- notifyListeners(SWT.Selection, event);
-}
-/**
- * Sets whether the widget wraps lines.
- * This overrides the creation style bit SWT.WRAP.
- * <p>
- *
- * @param wrap true=widget wraps lines, false=widget does not wrap lines
- * @since 2.0
- */
-public void setWordWrap(boolean wrap) {
- checkWidget();
-
- if (wrap != wordWrap) {
- ScrollBar horizontalBar = getHorizontalBar();
-
- wordWrap = wrap;
- if (wordWrap) {
- logicalContent = content;
- content = new WrappedContent(renderer, logicalContent);
- }
- else {
- content = logicalContent;
- }
- calculateContentWidth();
- horizontalScrollOffset = 0;
- if (horizontalBar != null) {
- horizontalBar.setVisible(!wordWrap);
- }
- setScrollBars();
- setCaretLocation();
- super.redraw();
- }
-}
-/**
- * Sets the caret location and scrolls the caret offset into view.
- */
-void showBidiCaret() {
- int line = content.getLineAtOffset(caretOffset);
- int lineOffset = content.getOffsetAtLine(line);
- int offsetInLine = caretOffset - lineOffset;
- String lineText = content.getLine(line);
- int xAtOffset = 0;
- boolean scrolled = false;
- GC gc = getGC();
- StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
- // getXAtOffset, inlined for better performance
- xAtOffset = getBidiTextPosition(lineText, offsetInLine, bidi) + leftMargin;
- if (offsetInLine > lineText.length()) {
- // offset is not on the line. return an x location one character
- // after the line to indicate the line delimiter.
- xAtOffset += lineEndSpaceWidth;
- }
- xAtOffset -= horizontalScrollOffset;
- //
- scrolled = showLocation(xAtOffset, line);
- if (scrolled == false) {
- setBidiCaretLocation(bidi);
- }
- gc.dispose();
-}
-/**
- * Sets the receiver's caret. Set the caret's height and location.
- *
- * </p>
- * @param caret the new caret for the receiver
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setCaret(Caret caret) {
- checkWidget ();
- super.setCaret(caret);
- if (caret != null) {
- if (isBidi() == false) {
- caret.setSize(caret.getSize().x, lineHeight);
- }
- setCaretLocation();
- if (isBidi()) {
- setBidiKeyboardLanguage();
- }
- }
-}
-/**
- * @see org.eclipse.swt.widgets.Control#setBackground
- */
-public void setBackground(Color color) {
- checkWidget();
- super.setBackground(color);
- background = color;
- redraw();
-}
-/**
- * Set the caret to indicate the current typing direction.
- */
-void setBidiCaretDirection() {
- Caret caret = getCaret();
- int direction = StyledTextBidi.getKeyboardLanguageDirection();
-
- if (caret == null || direction == caretDirection) {
- return;
- }
- caretDirection = direction;
- if (direction == SWT.DEFAULT) {
- caret.setImage(null);
- caret.setSize(caret.getSize().x, lineHeight);
- }
- else
- if (caretDirection == SWT.LEFT) {
- caret.setImage(leftCaretBitmap);
- }
- else
- if (caretDirection == SWT.RIGHT) {
- caret.setImage(rightCaretBitmap);
- }
-}
-/**
- * Moves the Caret to the current caret offset.
- * <p>
- *
- * @param bidi StyledTextBidi object to use for measuring.
- * May be left null in which case a new object will be created.
- */
-void setBidiCaretLocation(StyledTextBidi bidi) {
- int caretLine = getCaretLine();
-
- setBidiCaretLocation(bidi, caretLine);
-}
-/**
- * Moves the Caret to the current caret offset.
- * <p>
- *
- * @param bidi StyledTextBidi object to use for measuring.
- * May be left null in which case a new object will be created.
- * @param caretLine line the caret should be placed on. Relative to
- * first line in document
- */
-void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) {
- Caret caret = getCaret();
- String lineText = content.getLine(caretLine);
- int lineStartOffset = content.getOffsetAtLine(caretLine);
- int offsetInLine = caretOffset - lineStartOffset;
- GC gc = null;
-
- if (bidi == null) {
- gc = getGC();
- bidi = getStyledTextBidi(lineText, lineStartOffset, gc);
- }
- if (lastCaretDirection == SWT.NULL) {
- columnX = bidi.getTextPosition(offsetInLine) + leftMargin - horizontalScrollOffset;
- } else {
- columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset;
- }
- if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
- columnX -= (getCaretWidth() - 1);
- }
- if (caret != null) {
- setBidiCaretDirection();
- caret.setLocation(
- columnX,
- caretLine * lineHeight - verticalScrollOffset + topMargin);
- }
- if (gc != null) {
- gc.dispose();
- }
-}
-/**
- * Sets the BIDI coloring mode. When true the BIDI text display
- * algorithm is applied to segments of text that are the same
- * color.
- *
- * @param mode the new coloring mode
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * <p>
- * @deprecated use BidiSegmentListener instead.
- * </p>
- */
-public void setBidiColoring(boolean mode) {
- checkWidget();
- bidiColoring = mode;
-}
-/**
- * Switches the keyboard language according to the current editing
- * position and cursor direction.
- */
-void setBidiKeyboardLanguage() {
- int caretLine = getCaretLine();
- int lineStartOffset = content.getOffsetAtLine(caretLine);
- int offsetInLine = caretOffset - lineStartOffset;
- String lineText = content.getLine(caretLine);
- GC gc = getGC();
- StyledTextBidi bidi;
- int lineLength = lineText.length();
-
- // Don't supply the bold styles/font since we don't want to measure anything
- bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
- if (offsetInLine == 0) {
- bidi.setKeyboardLanguage(offsetInLine);
- }
- else
- if (offsetInLine >= lineLength) {
- offsetInLine = Math.min(offsetInLine, lineLength - 1);
- bidi.setKeyboardLanguage(offsetInLine);
- }
- else
- if (lastCaretDirection == ST.COLUMN_NEXT) {
- // continue with previous character type
- bidi.setKeyboardLanguage(offsetInLine - 1);
- }
- else {
- bidi.setKeyboardLanguage(offsetInLine);
- }
- gc.dispose();
-}
-/**
- * Moves the Caret to the current caret offset.
- * <p>
- *
- * @param newCaretX the new x location of the caret.
- * passed in for better performance when it has already been
- * calculated outside this method.
- * @param line index of the line the caret is on. Relative to
- * the first line in the document.
- */
-void setCaretLocation(int newCaretX, int line) {
- if (isBidi()) {
- setBidiCaretLocation(null, line);
- }
- else {
- Caret caret = getCaret();
-
- columnX = newCaretX;
- if (caret != null) {
- caret.setLocation(
- newCaretX,
- line * lineHeight - verticalScrollOffset + topMargin);
- }
- }
-}
-/**
- * Moves the Caret to the current caret offset.
- */
-void setCaretLocation() {
- if (isBidi()) {
- setBidiCaretLocation(null);
- }
- else {
- Caret caret = getCaret();
- int caretLine = getCaretLine();
- int lineStartOffset = content.getOffsetAtLine(caretLine);
-
- columnX = getXAtOffset(
- content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
- if (caret != null) {
- caret.setLocation(
- columnX,
- caretLine * lineHeight - verticalScrollOffset + topMargin);
- }
- }
-}
-/**
- * Sets the caret offset.
- *
- * @param offset caret offset, relative to the first character in the text.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
- * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul>
- */
-public void setCaretOffset(int offset) {
- checkWidget();
- int length = getCharCount();
-
- if (length > 0 && offset != caretOffset) {
- if (offset < 0) {
- caretOffset = 0;
- }
- else
- if (offset > length) {
- caretOffset = length;
- }
- else {
- if (isLineDelimiter(offset)) {
- // offset is inside a multi byte line delimiter. This is an
- // illegal operation and an exception is thrown. Fixes 1GDKK3R
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- caretOffset = offset;
- }
- // clear the selection if the caret is moved.
- // don't notify listeners about the selection change.
- clearSelection(false);
- }
- // always update the caret location. fixes 1G8FODP
- setCaretLocation();
- if (isBidi()) {
- setBidiKeyboardLanguage();
- }
-}
-/**
- * Copies the specified text range to the clipboard. The text will be placed
- * in the clipboard in plain text format and RTF format.
- * <p>
- *
- * @param start start index of the text
- * @param length length of text to place in clipboard
- *
- * @exception SWTError, see Clipboard.setContents
- * @see org.eclipse.swt.dnd.Clipboard.setContents
- */
-void setClipboardContent(int start, int length) throws SWTError {
- RTFTransfer rtfTransfer = RTFTransfer.getInstance();
- TextTransfer plainTextTransfer = TextTransfer.getInstance();
- RTFWriter rtfWriter = new RTFWriter(start, length);
- TextWriter plainTextWriter = new TextWriter(start, length);
- String rtfText = getPlatformDelimitedText(rtfWriter);
- String plainText = getPlatformDelimitedText(plainTextWriter);
-
- clipboard.setContents(
- new String[]{rtfText, plainText},
- new Transfer[]{rtfTransfer, plainTextTransfer});
-}
-/**
- * Sets the content implementation to use for text storage.
- * <p>
- *
- * @param content StyledTextContent implementation to use for text storage.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void setContent(StyledTextContent newContent) {
- checkWidget();
- if (newContent == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- if (content != null) {
- content.removeTextChangeListener(textChangeListener);
- }
- logicalContent = newContent;
- if (wordWrap) {
- content = new WrappedContent(renderer, logicalContent);
- }
- else {
- content = logicalContent;
- }
- content.addTextChangeListener(textChangeListener);
- reset();
-}
-/**
- * Sets the receiver's cursor to the cursor specified by the
- * argument. Overridden to handle the null case since the
- * StyledText widget uses an ibeam as its default cursor.
- *
- * @see org.eclipse.swt.widgets.Control#setCursor
- */
-public void setCursor (Cursor cursor) {
- if (cursor == null) {
- super.setCursor(ibeamCursor);
- } else {
- super.setCursor(cursor);
- }
-}
-/**
- * Sets whether the widget implements double click mouse behavior.
- * </p>
- *
- * @param enable if true double clicking a word selects the word, if false
- * double clicks have the same effect as regular mouse clicks.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setDoubleClickEnabled(boolean enable) {
- checkWidget();
- doubleClickEnabled = enable;
-}
-/**
- * Sets whether the widget content can be edited.
- * </p>
- *
- * @param editable if true content can be edited, if false content can not be
- * edited
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setEditable(boolean editable) {
- checkWidget();
- this.editable = editable;
-}
-/**
- * Sets a new font to render text with.
- * <p>
- * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
- * and the same baseline as regular fonts.
- * </p>
- *
- * @param font new font
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setFont(Font font) {
- checkWidget();
- int oldLineHeight = lineHeight;
-
- super.setFont(font);
- initializeRenderer();
- // keep the same top line visible. fixes 5815
- if (lineHeight != oldLineHeight) {
- setVerticalScrollOffset(verticalScrollOffset * lineHeight / oldLineHeight, true);
- claimBottomFreeSpace();
- }
- calculateContentWidth();
- calculateScrollBars();
- if (isBidi()) {
- caretDirection = SWT.NULL;
- createCaretBitmaps();
- setBidiCaretDirection();
- }
- else {
- Caret caret = getCaret();
- if (caret != null) {
- caret.setSize(caret.getSize().x, lineHeight);
- }
- }
- // always set the caret location. Fixes 6685
- setCaretLocation();
- super.redraw();
-}
-/**
- * @see org.eclipse.swt.widgets.Control#setForeground
- */
-public void setForeground(Color color) {
- checkWidget();
- super.setForeground(color);
- foreground = color;
- redraw();
-}
-/**
- * Sets the horizontal scroll offset relative to the start of the line.
- * Do nothing if there is no text set.
- * <p>
- * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
- * widget.
- * </p>
- *
- * @param offset horizontal scroll offset relative to the start
- * of the line, measured in character increments starting at 0, if
- * equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setHorizontalIndex(int offset) {
- checkWidget();
- int clientAreaWidth = getClientArea().width;
- if (getCharCount() == 0) {
- return;
- }
- if (offset < 0) {
- offset = 0;
- }
- offset *= getHorizontalIncrement();
- // allow any value if client area width is unknown or 0.
- // offset will be checked in resize handler.
- // don't use isVisible since width is known even if widget
- // is temporarily invisible
- if (clientAreaWidth > 0) {
- int width = lineCache.getWidth();
- // prevent scrolling if the content fits in the client area.
- // align end of longest line with right border of client area
- // if offset is out of range.
- if (offset > width - clientAreaWidth) {
- offset = Math.max(0, width - clientAreaWidth);
- }
- }
- scrollHorizontalBar(offset - horizontalScrollOffset);
-}
-/**
- * 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
- * is set in the widget.
- * </p>
- *
- * @param pixel horizontal pixel offset relative to the start
- * of the line.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @since 2.0
- */
-public void setHorizontalPixel(int pixel) {
- checkWidget();
- int clientAreaWidth = getClientArea().width;
- if (getCharCount() == 0) {
- return;
- }
- if (pixel < 0) {
- pixel = 0;
- }
- // allow any value if client area width is unknown or 0.
- // offset will be checked in resize handler.
- // don't use isVisible since width is known even if widget
- // is temporarily invisible
- if (clientAreaWidth > 0) {
- int width = lineCache.getWidth();
- // prevent scrolling if the content fits in the client area.
- // align end of longest line with right border of client area
- // if offset is out of range.
- if (pixel > width - clientAreaWidth) {
- pixel = Math.max(0, width - clientAreaWidth);
- }
- }
- scrollHorizontalBar(pixel - horizontalScrollOffset);
-}
-/**
- * Adjusts the maximum and the page size of the horizontal scroll bar
- * to reflect content width changes.
- */
-void setHorizontalScrollBar() {
- ScrollBar horizontalBar = getHorizontalBar();
-
- if (horizontalBar != null && horizontalBar.getVisible()) {
- final int INACTIVE = 1;
- Rectangle clientArea = getClientArea();
- // only set the real values if the scroll bar can be used
- // (ie. because the thumb size is less than the scroll maximum)
- // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
- if (clientArea.width < lineCache.getWidth()) {
- horizontalBar.setValues(
- horizontalBar.getSelection(),
- horizontalBar.getMinimum(),
- lineCache.getWidth(), // maximum
- clientArea.width - leftMargin - rightMargin, // thumb size
- horizontalBar.getIncrement(),
- clientArea.width - leftMargin - rightMargin); // page size
- }
- else
- if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) {
- horizontalBar.setValues(
- horizontalBar.getSelection(),
- horizontalBar.getMinimum(),
- INACTIVE,
- INACTIVE,
- horizontalBar.getIncrement(),
- INACTIVE);
- }
- }
-}
-/**
- * Sets the background color of the specified lines.
- * The background color is drawn for the width of the widget. All
- * line background colors are discarded when setText is called.
- * The text background color if defined in a StyleRange overlays the
- * line background color. Should not be called if a LineBackgroundListener
- * has been set since the listener maintains the line backgrounds.
- * <p>
- * Line background colors are maintained relative to the line text, not the
- * line index that is specified in this method call.
- * During text changes, when entire lines are inserted or removed, the line
- * background colors that are associated with the lines after the change
- * will "move" with their respective text. An entire line is defined as
- * extending from the first character on a line to the last and including the
- * line delimiter.
- * </p>
- * <p>
- * When two lines are joined by deleting a line delimiter, the top line
- * background takes precedence and the color of the bottom line is deleted.
- * For all other text changes line background colors will remain unchanged.
- * </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
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
- * </ul>
- */
-public void setLineBackground(int startLine, int lineCount, Color background) {
- checkWidget();
- int partialBottomIndex = getPartialBottomIndex();
-
- // this API can not be used if the client is providing the line background
- if (userLineBackground) {
- return;
- }
- if (startLine < 0 || startLine + lineCount > logicalContent.getLineCount()) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- defaultLineStyler.setLineBackground(startLine, lineCount, background);
- // do nothing if redraw range is completely invisible
- if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
- return;
- }
- // only redraw visible lines
- if (startLine < topIndex) {
- lineCount -= topIndex - startLine;
- startLine = topIndex;
- }
- if (startLine + lineCount - 1 > partialBottomIndex) {
- lineCount = partialBottomIndex - startLine + 1;
- }
- startLine -= topIndex;
- super.redraw(
- leftMargin, startLine * lineHeight + topMargin,
- getClientArea().width - leftMargin - rightMargin, lineCount * lineHeight, true);
-}
-/**
- * Flips selection anchor based on word selection direction.
- */
-void setMouseWordSelectionAnchor() {
- if (mouseDoubleClick == false) {
- return;
- }
- if (caretOffset < doubleClickSelection.x) {
- selectionAnchor = doubleClickSelection.y;
- }
- else
- if (caretOffset > doubleClickSelection.y) {
- selectionAnchor = doubleClickSelection.x;
- }
-}
-/**
- * Adjusts the maximum and the page size of the scroll bars to
- * reflect content width/length changes.
- */
-void setScrollBars() {
- ScrollBar verticalBar = getVerticalBar();
-
- if (verticalBar != null) {
- Rectangle clientArea = getClientArea();
- final int INACTIVE = 1;
- int maximum = content.getLineCount() * getVerticalIncrement();
-
- // only set the real values if the scroll bar can be used
- // (ie. because the thumb size is less than the scroll maximum)
- // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
- if (clientArea.height < maximum) {
- verticalBar.setValues(
- verticalBar.getSelection(),
- verticalBar.getMinimum(),
- maximum,
- clientArea.height, // thumb size
- verticalBar.getIncrement(),
- clientArea.height); // page size
- }
- else
- if (verticalBar.getThumb() != INACTIVE || verticalBar.getMaximum() != INACTIVE) {
- verticalBar.setValues(
- verticalBar.getSelection(),
- verticalBar.getMinimum(),
- INACTIVE,
- INACTIVE,
- verticalBar.getIncrement(),
- INACTIVE);
- }
- }
- setHorizontalScrollBar();
-}
-/**
- * Sets the selection to the given position and scrolls it into view. Equivalent to setSelection(start,start).
- * <p>
- *
- * @param start new caret position
- * @see #setSelection(int,int)
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when start is outside the widget content
- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
- * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul>
- */
-public void setSelection(int start) {
- // 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
- * N+1 caret positions, ranging from 0..N
- * </p>
- *
- * @param point x=selection start offset, y=selection end offset
- * The caret will be placed at the selection start when x > y.
- * @see #setSelection(int,int)
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when point is null</li>
- * <li>ERROR_INVALID_RANGE when start or end is outside the widget content
- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
- * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul>
- */
-public void setSelection(Point point) {
- checkWidget();
- if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
- setSelection(point.x, point.y);
-}
-/**
- * Sets the receiver's selection background color to the color specified
- * by the argument, or to the default system color for the control
- * if the argument is null.
- *
- * @param color the new color (or null)
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @since 2.1
- */
-public void setSelectionBackground (Color color) {
- checkWidget ();
- if (color != null) {
- if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- selectionBackground = color;
- redraw();
-}
-/**
- * Sets the receiver's selection foreground color to the color specified
- * by the argument, or to the default system color for the control
- * if the argument is null.
- *
- * @param color the new color (or null)
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @since 2.1
- */
-public void setSelectionForeground (Color color) {
- checkWidget ();
- if (color != null) {
- if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- selectionForeground = color;
- redraw();
-}
-/**
- * Sets the selection and scrolls it into view.
- * <p>
- * Indexing is zero based. Text selections are specified in terms of
- * caret positions. In a text widget that contains N characters, there are
- * N+1 caret positions, ranging from 0..N
- * </p>
- *
- * @param start selection start offset. The caret will be placed at the
- * selection start when start > end.
- * @param end selection end offset
- * @see #setSelectionRange(int,int)
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when start or end is outside the widget content
- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
- * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul>
- */
-public void setSelection(int start, int end) {
- // checkWidget test done in setSelectionRange
- setSelectionRange(start, end - start);
- showSelection();
-}
-/**
- * Sets the selection. The new selection may not be visible. Call showSelection to scroll
- * the selection into view.
- * <p>
- *
- * @param start offset of the first selected character, start >= 0 must be true.
- * @param length number of characters to select, 0 <= start + length <= getCharCount()
- * must be true.
- * A negative length places the caret at the visual start of the selection.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when the range specified by start and length is outside the widget content
- * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
- * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
- * </ul>
- */
-public void setSelectionRange(int start, int length) {
- checkWidget();
- int contentLength = getCharCount();
- int end = start + length;
-
- if (start < 0 || end < 0 || start > contentLength || end > contentLength) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- if (isLineDelimiter(start) || isLineDelimiter(end)) {
- // the start offset or end offset of the selection range is inside a
- // multi byte line delimiter. This is an illegal operation and an exception
- // is thrown. Fixes 1GDKK3R
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- internalSetSelection(start, length, false);
- // always update the caret location. fixes 1G8FODP
- setCaretLocation();
- if (isBidi()) {
- setBidiKeyboardLanguage();
- }
-}
-/**
- * Sets the selection.
- * The new selection may not be visible. Call showSelection to scroll
- * the selection into view.
- * <p>
- *
- * @param start offset of the first selected character, start >= 0 must be true.
- * @param length number of characters to select, 0 <= start + length
- * <= getCharCount() must be true.
- * A negative length places the caret at the selection start.
- * @param sendEvent a Selection event is sent when set to true and when
- * the selection is reset.
- */
-void internalSetSelection(int start, int length, boolean sendEvent) {
- int end = start + length;
-
- if (start > end) {
- int temp = end;
- end = start;
- start = temp;
- }
- // is the selection range different or is the selection direction
- // different?
- if (selection.x != start || selection.y != end ||
- (length > 0 && selectionAnchor != selection.x) ||
- (length < 0 && selectionAnchor != selection.y)) {
- clearSelection(sendEvent);
- if (length < 0) {
- selectionAnchor = selection.y = end;
- caretOffset = selection.x = start;
- }
- else {
- selectionAnchor = selection.x = start;
- caretOffset = selection.y = end;
- }
- internalRedrawRange(selection.x, selection.y - selection.x, true);
- }
-}
-/**
- * Adds the specified style. The new style overwrites existing styles for the
- * specified range. Existing style ranges are adjusted if they partially
- * overlap with the new style, To clear an individual style, call setStyleRange
- * with a StyleRange that has null attributes.
- * <p>
- * Should not be called if a LineStyleListener has been set since the
- * listener maintains the styles.
- * </p>
- *
- * @param range StyleRange object containing the style information.
- * Overwrites the old style in the given range. May be null to delete
- * all styles.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
- * </ul>
- */
-public void setStyleRange(StyleRange range) {
- checkWidget();
-
- // this API can not be used if the client is providing the line styles
- if (userLineStyle) {
- return;
- }
- // check the range, make sure it falls within the range of the
- // text
- if (range != null && range.start + range.length > content.getCharCount()) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- if (range != null) {
- boolean redrawFirstLine = false;
- boolean redrawLastLine = false;
- int firstLine = content.getLineAtOffset(range.start);
- int lastLine = content.getLineAtOffset(range.start + range.length);
-
- // if the style is not visible, there is no need to redraw
- boolean redrawLines = isAreaVisible(firstLine, lastLine);
-
- if (!redrawLines) {
- defaultLineStyler.setStyleRange(range);
- lineCache.reset(firstLine, lastLine - firstLine + 1, true);
- } else {
- // the first and last line needs to be redrawn completely if the
- // font style is changing from SWT.NORMAL to something else or
- // vice versa. fixes 1G7M5WE.
- int firstLineOffset = content.getOffsetAtLine(firstLine);
- int lastLineOffset = content.getOffsetAtLine(lastLine);
- if (isBidi()) {
- if (firstLine != lastLine) {
- redrawFirstLine = true;
- }
- redrawLastLine = true;
- } else {
- redrawFirstLine = isRedrawFirstLine(new StyleRange[] {range}, firstLine, firstLineOffset);
- if (lastLine != firstLine) {
- redrawLastLine = isRedrawLastLine(new StyleRange[] {range}, lastLine, lastLineOffset);
- }
- }
- defaultLineStyler.setStyleRange(range);
- // reset all lines affected by the style change but let the redraw
- // recalculate only those that are visible.
- lineCache.reset(firstLine, lastLine - firstLine + 1, true);
- internalRedrawRange(range.start, range.length, true);
- if (redrawFirstLine) {
- redrawLine(firstLine, range.start - firstLineOffset);
- }
- if (redrawLastLine) {
- redrawLine(lastLine, 0);
- }
- }
- } else {
- // clearing all styles
- defaultLineStyler.setStyleRange(range);
- lineCache.reset(0, content.getLineCount(), false);
- redraw();
- }
-
- // make sure that the caret is positioned correctly.
- // caret location may change if font style changes.
- // fixes 1G8FODP
- setCaretLocation();
-}
-/**
- * Sets styles to be used for rendering the widget content. All styles
- * in the widget will be replaced with the given set of styles.
- * <p>
- * Should not be called if a LineStyleListener has been set since the
- * 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 do overlap. Must not be null.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li>
- * </ul>
- */
-public void setStyleRanges(StyleRange[] ranges) {
- checkWidget();
- // this API can not be used if the client is providing the line styles
- if (userLineStyle) {
- return;
- }
- if (ranges == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- // check the last range, make sure it falls within the range of the
- // current text
- if (ranges.length != 0) {
- StyleRange last = ranges[ranges.length-1];
- int lastEnd = last.start + last.length;
- int firstLine = content.getLineAtOffset(ranges[0].start);
- int lastLine;
- if (lastEnd > content.getCharCount()) {
- SWT.error(SWT.ERROR_INVALID_RANGE);
- }
- lastLine = content.getLineAtOffset(lastEnd);
- // reset all lines affected by the style change
- lineCache.reset(firstLine, lastLine - firstLine + 1, true);
- }
- else {
- // reset all lines
- lineCache.reset(0, content.getLineCount(), false);
- }
- defaultLineStyler.setStyleRanges(ranges);
- redraw(); // should only redraw affected area to avoid flashing
- // make sure that the caret is positioned correctly.
- // caret location may change if font style changes.
- // fixes 1G8FODP
- setCaretLocation();
-}
-/**
- * Sets the tab width.
- * <p>
- *
- * @param tabs tab width measured in characters.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setTabs(int tabs) {
- checkWidget();
- tabLength = tabs;
- renderer.setTabLength(tabLength);
- if (caretOffset > 0) {
- caretOffset = 0;
- if (isBidi()) {
- showBidiCaret();
- }
- else {
- showCaret();
- }
- clearSelection(false);
- }
- // reset all line widths when the tab width changes
- lineCache.reset(0, content.getLineCount(), false);
- redraw();
-}
-/**
- * Sets the widget content.
- * If the widget has the SWT.SINGLE style and "text" contains more than
- * one line, only the first line is rendered but the text is stored
- * unchanged. A subsequent call to getText will return the same text
- * that was set.
- * <p>
- * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
- * style is used.
- * </p>
- *
- * @param text new widget content. Replaces existing content. Line styles
- * that were set using StyledText API are discarded. The
- * current selection is also discarded.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when string is null</li>
- * </ul>
- */
-public void setText(String text) {
- checkWidget();
- Event event = new Event();
-
- if (text == null) {
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- }
- event.start = 0;
- event.end = getCharCount();
- event.text = text;
- event.doit = true;
- notifyListeners(SWT.Verify, event);
- if (event.doit) {
- StyledTextEvent styledTextEvent = null;
-
- if (isListening(ExtendedModify)) {
- styledTextEvent = new StyledTextEvent(logicalContent);
- styledTextEvent.start = event.start;
- styledTextEvent.end = event.start + event.text.length();
- styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
- }
- content.setText(event.text);
- notifyListeners(SWT.Modify, event);
- if (styledTextEvent != null) {
- notifyListeners(ExtendedModify, styledTextEvent);
- }
- }
-}
-/**
- * Sets the text limit.
- * <p>
- * The text limit specifies the amount of text that
- * the user can type into the widget.
- * </p>
- *
- * @param limit the new text limit.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
- * </ul>
- */
-public void setTextLimit(int limit) {
- checkWidget();
- if (limit == 0) {
- SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
- }
- textLimit = limit;
-}
-/**
- * Sets the top index. Do nothing if there is no text set.
- * <p>
- * The top index is the index of the line that is currently at the top
- * of the widget. The top index changes when the widget is scrolled.
- * Indexing starts from zero.
- * Note: The top index is reset to 0 when new text is set in the widget.
- * </p>
- *
- * @param index new top index. Must be between 0 and
- * getLineCount() - fully visible lines per page. If no lines are fully
- * visible the maximum value is getLineCount() - 1. An out of range
- * index will be adjusted accordingly.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setTopIndex(int topIndex) {
- checkWidget();
- int lineCount = logicalContent.getLineCount();
- int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
-
- if (getCharCount() == 0) {
- return;
- }
- if (topIndex < 0) {
- topIndex = 0;
- }
- else
- if (topIndex > lineCount - pageSize) {
- topIndex = lineCount - pageSize;
- }
- if (wordWrap) {
- int logicalLineOffset = logicalContent.getOffsetAtLine(topIndex);
- topIndex = content.getLineAtOffset(logicalLineOffset);
- }
- setVerticalScrollOffset(topIndex * getVerticalIncrement(), true);
-}
-/**
- * Sets the top pixel offset. Do nothing if there is no text set.
- * <p>
- * The top pixel offset is the vertical pixel offset of the widget. The
- * widget is scrolled so that the given pixel position is at the top.
- * The top index is adjusted to the corresponding top line.
- * Note: The top pixel is reset to 0 when new text is set in the widget.
- * </p>
- *
- * @param pixel new top pixel offset. Must be between 0 and
- * (getLineCount() - visible lines per page) / getLineHeight()). An out
- * of range offset will be adjusted accordingly.
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- * @since 2.0
- */
-public void setTopPixel(int pixel) {
- checkWidget();
- int lineCount =content.getLineCount();
- int height = getClientArea().height;
- int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
-
- if (getCharCount() == 0) {
- return;
- }
- if (pixel < 0) {
- pixel = 0;
- }
- else
- if (pixel > maxTopPixel) {
- pixel = maxTopPixel;
- }
- setVerticalScrollOffset(pixel, true);
-}
-/**
- * Scrolls the widget vertically.
- * <p>
- *
- * @param pixelOffset the new vertical scroll offset
- * @param adjustScrollBar
- * true= the scroll thumb will be moved to reflect the new scroll offset.
- * false = the scroll thumb will not be moved
- * @return
- * true=the widget was scrolled
- * false=the widget was not scrolled, the given offset is not valid.
- */
-boolean setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) {
- Rectangle clientArea;
- ScrollBar verticalBar = getVerticalBar();
-
- if (pixelOffset == verticalScrollOffset) {
- return false;
- }
- if (verticalBar != null && adjustScrollBar) {
- verticalBar.setSelection(pixelOffset);
- }
- clientArea = getClientArea();
- scroll(
- 0, 0, // destination x, y
- 0, pixelOffset - verticalScrollOffset, // source x, y
- clientArea.width, clientArea.height, true);
-
- verticalScrollOffset = pixelOffset;
- calculateTopIndex();
- setCaretLocation();
- return true;
-}
-/**
- * Scrolls the specified location into view.
- * <p>
- *
- * @param x the x coordinate that should be made visible.
- * @param line the line that should be made visible. Relative to the
- * first line in the document.
- * @return
- * true=the widget was scrolled to make the specified location visible.
- * false=the specified location is already visible, the widget was
- * not scrolled.
- */
-boolean showLocation(int x, int line) {
- int clientAreaWidth = getClientArea().width - leftMargin - rightMargin;
- int verticalIncrement = getVerticalIncrement();
- int horizontalIncrement = clientAreaWidth / 4;
- boolean scrolled = false;
-
- if (x < leftMargin) {
- // always make 1/4 of a page visible
- x = Math.max(horizontalScrollOffset * -1, x - horizontalIncrement);
- scrolled = scrollHorizontalBar(x);
- }
- else
- if (x >= clientAreaWidth) {
- // always make 1/4 of a page visible
- x = Math.min(lineCache.getWidth() - horizontalScrollOffset, x + horizontalIncrement);
- scrolled = scrollHorizontalBar(x - clientAreaWidth);
- }
- if (line < topIndex) {
- scrolled = setVerticalScrollOffset(line * verticalIncrement, true);
- }
- else
- if (line > getBottomIndex()) {
- scrolled = setVerticalScrollOffset((line + 1) * verticalIncrement - getClientArea().height, true);
- }
- return scrolled;
-}
-/**
- * Sets the caret location and scrolls the caret offset into view.
- */
-void showCaret() {
- int caretLine = content.getLineAtOffset(caretOffset);
-
- showCaret(caretLine);
-}
-/**
- * Sets the caret location and scrolls the caret offset into view.
- */
-void showCaret(int caretLine) {
- int lineOffset = content.getOffsetAtLine(caretLine);
- String lineText = content.getLine(caretLine);
- int offsetInLine = caretOffset - lineOffset;
- int xAtOffset = getXAtOffset(lineText, caretLine, offsetInLine);
- boolean scrolled = showLocation(xAtOffset, caretLine);
- boolean setWrapCaretLocation = false;
- Caret caret = getCaret();
-
- if (wordWrap && caret != null) {
- int caretY = caret.getLocation().y;
- if ((caretY + verticalScrollOffset) / getVerticalIncrement() - 1 != caretLine) {
- setWrapCaretLocation = true;
- }
- }
- if (scrolled == false || setWrapCaretLocation) {
- // set the caret location if a scroll operation did not set it (as a
- // sideeffect of scrolling) or when in word wrap mode and the caret
- // line was explicitly specified (i.e., because getWrapCaretLine does
- // not return the desired line causing scrolling to not set it correctly)
- setCaretLocation(xAtOffset, caretLine);
- }
- if (isBidi()) {
- setBidiKeyboardLanguage();
- }
-}
-/**
- * Scrolls the specified offset into view.
- * <p>
- *
- * @param offset offset that should be scolled into view
- */
-void showOffset(int offset) {
- int line = content.getLineAtOffset(offset);
- int lineOffset = content.getOffsetAtLine(line);
- int offsetInLine = offset - lineOffset;
- String lineText = content.getLine(line);
- int xAtOffset = getXAtOffset(lineText, line, offsetInLine);
-
- showLocation(xAtOffset, line);
-}
-/**
-/**
- * Scrolls the selection into view. The end of the selection will be scrolled into
- * view. Note that if a right-to-left selection exists, the end of the selection is the
- * visual beginning of the selection (i.e., where the caret is located).
- * <p>
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void showSelection() {
- checkWidget();
- boolean selectionFits;
- int startOffset, startLine, startX, endOffset, endLine, endX, offsetInLine;
-
- // is selection from right-to-left?
- boolean rightToLeft = caretOffset == selection.x;
-
- if (rightToLeft) {
- startOffset = selection.y;
- endOffset = selection.x;
- } else {
- startOffset = selection.x;
- endOffset = selection.y;
- }
-
- // calculate the logical start and end values for the selection
- startLine = content.getLineAtOffset(startOffset);
- offsetInLine = startOffset - content.getOffsetAtLine(startLine);
- startX = getXAtOffset(content.getLine(startLine), startLine, offsetInLine);
- endLine = content.getLineAtOffset(endOffset);
- offsetInLine = endOffset - content.getOffsetAtLine(endLine);
- endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);
-
- // can the selection be fully displayed within the widget's visible width?
- int w = getClientArea().width;
- if (rightToLeft) {
- selectionFits = startX - endX <= w;
- } else {
- selectionFits = endX - startX <= w;
- }
-
- if (selectionFits) {
- // show as much of the selection as possible by first showing
- // the start of the selection
- showLocation(startX, startLine);
- // endX value could change if showing startX caused a scroll to occur
- endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);
- showLocation(endX, endLine);
- } else {
- // just show the end of the selection since the selection start
- // will not be visible
- showLocation(endX, endLine);
- }
-}
-/**
- * Updates the caret direction when a delete operation occured based on
- * the type of the delete operation (next/previous character) and the
- * caret location (at a direction boundary or inside a direction segment).
- * The intent is to place the caret at the visual location where a
- * character was deleted.
- * <p>
- *
- * @param isBackspace true=the previous character was deleted, false=the
- * character next to the caret location was deleted
- * @param isDirectionBoundary true=the caret is between a R2L and L2R segment,
- * false=the caret is within a direction segment
- */
-void updateBidiDirection(boolean isBackspace, boolean isDirectionBoundary) {
- if (isDirectionBoundary) {
- if (isBackspace) {
- // Deleted previous character (backspace) at a direction boundary
- // Go to direction segment of deleted character
- lastCaretDirection = ST.COLUMN_NEXT;
- }
- else {
- // Deleted next character. Go to direction segment of deleted character
- lastCaretDirection = ST.COLUMN_PREVIOUS;
- }
- }
- else {
- if (isBackspace) {
- // Delete previous character inside direction segment (i.e., not at a direction boundary)
- lastCaretDirection = ST.COLUMN_PREVIOUS;
- }
- else {
- // Deleted next character.
- lastCaretDirection = ST.COLUMN_NEXT;
- }
- }
-}
-/**
- * Updates the selection and caret position depending on the text change.
- * 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
- * it is left unchanged.
- * <p>
- *
- * @param startOffset offset of the text change
- * @param replacedLength length of text being replaced
- * @param newLength length of new text
- */
-void updateSelection(int startOffset, int replacedLength, int newLength) {
- if (selection.y <= startOffset) {
- // selection ends before text change
- return;
- }
- if (selection.x < startOffset) {
- // clear selection fragment before text change
- internalRedrawRange(selection.x, startOffset - selection.x, true);
- }
- if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
- // clear selection fragment after text change.
- // do this only when the selection is actually affected by the
- // change. Selection is only affected if it intersects the change (1GDY217).
- int netNewLength = newLength - replacedLength;
- int redrawStart = startOffset + newLength;
- internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart, true);
- }
- if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
- // selection intersects replaced text. set caret behind text change
- internalSetSelection(startOffset + newLength, 0, true);
- // always update the caret location. fixes 1G8FODP
- setCaretLocation();
- }
- else {
- // move selection to keep same text selected
- internalSetSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true);
- // always update the caret location. fixes 1G8FODP
- setCaretLocation();
- }
-}
-/**
- * Rewraps all lines
- * <p>
- *
- * @param oldClientAreaWidth client area width before resize
- * occurred
- */
-void wordWrapResize(int oldClientAreaWidth) {
- WrappedContent wrappedContent = (WrappedContent) content;
- int newTopIndex;
-
- // all lines are wrapped and no rewrap required if widget has already
- // been visible, client area is now wider and visual (wrapped) line
- // count equals logical line count.
- if (oldClientAreaWidth != 0 && clientAreaWidth > oldClientAreaWidth &&
- wrappedContent.getLineCount() == logicalContent.getLineCount()) {
- return;
- }
- wrappedContent.wrapLines();
-
- // adjust the top index so that top line remains the same
- newTopIndex = content.getLineAtOffset(topOffset);
- // topOffset is the beginning of the top line. therefore it
- // needs to be adjusted because in a wrapped line this is also
- // the end of the preceeding line.
- if (newTopIndex < content.getLineCount() - 1 &&
- topOffset == content.getOffsetAtLine(newTopIndex + 1)) {
- newTopIndex++;
- }
- if (newTopIndex != topIndex) {
- ScrollBar verticalBar = getVerticalBar();
- // adjust index and pixel offset manually instead of calling
- // setVerticalScrollOffset because the widget does not actually need
- // to be scrolled. causes flash otherwise.
- verticalScrollOffset += (newTopIndex - topIndex) * getVerticalIncrement();
- // verticalScrollOffset may become negative if first line was
- // partially visible and second line was top line. prevent this from
- // happening to fix 8503.
- if (verticalScrollOffset < 0) {
- verticalScrollOffset = 0;
- }
- topIndex = newTopIndex;
- topOffset = content.getOffsetAtLine(topIndex);
- if (verticalBar != null) {
- verticalBar.setSelection(verticalScrollOffset);
- }
- }
- // caret may be on a different line after a rewrap.
- // call setCaretLocation after fixing vertical scroll offset.
- setCaretLocation();
- // word wrap may have changed on one of the visible lines
- super.redraw();
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.printing.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A StyledText is an editable user interface object that displays lines
+ * of text. The following style attributes can be defined for the text:
+ * <ul>
+ * <li>foreground color
+ * <li>background color
+ * <li>font style (bold, regular)
+ * </ul>
+ * <p>
+ * In addition to text style attributes, the background color of a line may
+ * 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
+ * not define your own listener and use the StyledText API. The following
+ * StyledText API is not supported if you have defined a LineStyleListener:
+ * <ul>
+ * <li>getStyleRangeAtOffset(int)
+ * <li>getStyleRanges()
+ * <li>replaceStyleRanges(int,int,StyleRange[])
+ * <li>setStyleRange(StyleRange)
+ * <li>setStyleRanges(StyleRange[])
+ * </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
+ * LineBackgroundListener:
+ * <ul>
+ * <li>getLineBackground(int)
+ * <li>setLineBackground(int,int,Color)
+ * </ul>
+ * </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.
+ * </p>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b><dd>FULL_SELECTION, MULTI, READ_ONLY, SINGLE, WRAP
+ * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetSegments, LineGetStyle, Modify, Selection, Verify, VerifyKey
+ * </dl>
+ */
+public class StyledText extends Canvas {
+ static final char TAB = '\t';
+ static final String PlatformLineDelimiter = System.getProperty("line.separator");
+ static final int BIDI_CARET_WIDTH = 4;
+ static final int XINSET = BIDI_CARET_WIDTH - 1;
+ static final int DEFAULT_WIDTH = 64;
+ static final int DEFAULT_HEIGHT = 64;
+
+ static final int ExtendedModify = 3000;
+ static final int LineGetBackground = 3001;
+ static final int LineGetStyle = 3002;
+ static final int TextChanging = 3003;
+ static final int TextSet = 3004;
+ static final int VerifyKey = 3005;
+ static final int TextChanged = 3006;
+ static final int LineGetSegments = 3007;
+
+ Color selectionBackground; // selection background color
+ Color selectionForeground; // selection foreground color
+ StyledTextContent logicalContent; // native content (default or user specified)
+ StyledTextContent content; // line wrapping content, same as logicalContent if word wrap is off
+ DisplayRenderer renderer;
+ TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent
+ DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered
+ LineCache lineCache;
+ boolean userLineStyle = false; // true=widget is using a user defined line style listener for line styles. false=widget is using the default line styler to store line styles
+ boolean userLineBackground = false; // true=widget is using a user defined line background listener for line backgrounds. false=widget is using the default line styler to store line backgrounds
+ int verticalScrollOffset = 0; // pixel based
+ int horizontalScrollOffset = 0; // pixel based
+ int topIndex = 0; // top visible line
+ int topOffset = 0; // offset of first character in top line
+ int clientAreaHeight = 0; // the client area height. Needed to calculate content width for new
+ // visible lines during Resize callback
+ int clientAreaWidth = 0; // the client area width. Needed during Resize callback to determine
+ // if line wrap needs to be recalculated
+ int lineHeight; // line height=font height
+ int tabLength = 4; // number of characters in a tab
+ int lineEndSpaceWidth; // space, in pixel, used to indicated a selected line break
+ int leftMargin = 1;
+ int topMargin = 1;
+ int rightMargin = 2;
+ int bottomMargin = 2;
+ Cursor ibeamCursor;
+ int columnX; // keep track of the horizontal caret position
+ // when changing lines/pages. Fixes bug 5935
+ int caretOffset = 0;
+ Point selection = new Point(0, 0); // x is character offset, y is length
+ int selectionAnchor; // position of selection anchor. 0 based offset from beginning of text
+ Point doubleClickSelection; // selection after last mouse double click
+ boolean editable = true;
+ boolean wordWrap = false;
+ boolean doubleClickEnabled = true; // see getDoubleClickEnabled
+ boolean overwrite = false; // insert/overwrite edit mode
+ int textLimit = -1; // limits the number of characters the user can type in the widget. Unlimited by default.
+ Hashtable keyActionMap = new Hashtable();
+ Color background = null; // workaround for bug 4791
+ Color foreground = null; //
+ Clipboard clipboard;
+ boolean mouseDoubleClick = false; // true=a double click ocurred. Don't do mouse swipe selection.
+ int autoScrollDirection = SWT.NULL; // the direction of autoscrolling (up, down, right, left)
+ int lastTextChangeStart; // cache data of the
+ int lastTextChangeNewLineCount; // last text changing
+ int lastTextChangeNewCharCount; // event for use in the
+ int lastTextChangeReplaceLineCount; // text changed handler
+ int lastTextChangeReplaceCharCount;
+ boolean isBidi;
+ boolean bidiColoring = false; // apply the BIDI algorithm on text segments of the same color
+ Image leftCaretBitmap = null;
+ Image rightCaretBitmap = null;
+ int caretDirection = SWT.NULL;
+ PaletteData caretPalette = null;
+ int lastCaretDirection = SWT.NULL;
+ boolean isCarbon; // flag set to true on Mac OSX
+
+ /**
+ * The Printing class implements printing of a range of text.
+ * An instance of <class>Printing </class> is returned in the
+ * StyledText#print(Printer) API. The run() method may be
+ * invoked from any thread.
+ */
+ class Printing implements Runnable {
+ final static int LEFT = 0; // left aligned header/footer segment
+ final static int CENTER = 1; // centered header/footer segment
+ final static int RIGHT = 2; // right aligned header/footer segment
+
+ Printer printer;
+ PrintRenderer renderer;
+ StyledTextPrintOptions printOptions;
+ StyledTextContent printerContent; // copy of the widget content
+ Rectangle clientArea; // client area to print on
+ Font printerFont;
+ FontData displayFontData;
+ Hashtable printerColors; // printer color cache for line backgrounds and style
+ Hashtable lineBackgrounds = new Hashtable(); // cached line backgrounds
+ Hashtable lineStyles = new Hashtable(); // cached line styles
+ Hashtable bidiSegments = new Hashtable(); // cached bidi segments when running on a bidi platform
+ GC gc; // printer GC
+ int pageWidth; // width of a printer page in pixels
+ int startPage; // first page to print
+ int endPage; // last page to print
+ int pageSize; // number of lines on a page
+ int startLine; // first (wrapped) line to print
+ int endLine; // last (wrapped) line to print
+ boolean singleLine; // widget single line mode
+ Point selection = null; // selected text
+
+ /**
+ * Creates an instance of <class>Printing</class>.
+ * Copies the widget content and rendering data that needs
+ * to be requested from listeners.
+ * </p>
+ * @param parent StyledText widget to print.
+ * @param printer printer device to print on.
+ * @param printOptions print options
+ */
+ Printing(StyledText parent, Printer printer, StyledTextPrintOptions printOptions) {
+ PrinterData data = printer.getPrinterData();
+
+ this.printer = printer;
+ this.printOptions = printOptions;
+ singleLine = parent.isSingleLine();
+ startPage = 1;
+ endPage = Integer.MAX_VALUE;
+ if (data.scope == PrinterData.PAGE_RANGE) {
+ startPage = data.startPage;
+ endPage = data.endPage;
+ if (endPage < startPage) {
+ int temp = endPage;
+ endPage = startPage;
+ startPage = temp;
+ }
+ } if (data.scope == PrinterData.SELECTION) {
+ selection = parent.getSelectionRange();
+ }
+
+ displayFontData = getFont().getFontData()[0];
+ copyContent(parent.getContent());
+ cacheLineData(printerContent);
+ }
+ /**
+ * Caches the bidi segments of the given line.
+ * </p>
+ * @param lineOffset offset of the line to cache bidi segments for.
+ * Relative to the start of the document.
+ * @param line line to cache bidi segments for.
+ */
+ void cacheBidiSegments(int lineOffset, String line) {
+ int[] segments = getBidiSegments(lineOffset, line);
+
+ if (segments != null) {
+ bidiSegments.put(new Integer(lineOffset), segments);
+ }
+ }
+ /**
+ * Caches the line background color of the given line.
+ * </p>
+ * @param lineOffset offset of the line to cache the background
+ * color for. Relative to the start of the document.
+ * @param line line to cache the background color for
+ */
+ void cacheLineBackground(int lineOffset, String line) {
+ StyledTextEvent event = getLineBackgroundData(lineOffset, line);
+
+ if (event != null) {
+ lineBackgrounds.put(new Integer(lineOffset), event);
+ }
+ }
+ /**
+ * Caches all line data that needs to be requested from a listener.
+ * </p>
+ * @param printerContent <class>StyledTextContent</class> to request
+ * line data for.
+ */
+ void cacheLineData(StyledTextContent printerContent) {
+ for (int i = 0; i < printerContent.getLineCount(); i++) {
+ int lineOffset = printerContent.getOffsetAtLine(i);
+ String line = printerContent.getLine(i);
+
+ if (printOptions.printLineBackground) {
+ cacheLineBackground(lineOffset, line);
+ }
+ if (printOptions.printTextBackground ||
+ printOptions.printTextForeground ||
+ printOptions.printTextFontStyle) {
+ cacheLineStyle(lineOffset, line);
+ }
+ if (isBidi()) {
+ cacheBidiSegments(lineOffset, line);
+ }
+ }
+ }
+ /**
+ * Caches all line styles of the given line.
+ * </p>
+ * @param lineOffset offset of the line to cache the styles for.
+ * Relative to the start of the document.
+ * @param line line to cache the styles for.
+ */
+ void cacheLineStyle(int lineOffset, String line) {
+ StyledTextEvent event = getLineStyleData(lineOffset, line);
+
+ if (event != null) {
+ StyleRange[] styles = event.styles;
+ for (int i = 0; i < styles.length; i++) {
+ StyleRange styleCopy = null;
+ if (printOptions.printTextBackground == false && styles[i].background != null) {
+ styleCopy = (StyleRange) styles[i].clone();
+ styleCopy.background = null;
+ }
+ if (printOptions.printTextForeground == false && styles[i].foreground != null) {
+ if (styleCopy == null) {
+ styleCopy = (StyleRange) styles[i].clone();
+ }
+ styleCopy.foreground = null;
+ }
+ if (printOptions.printTextFontStyle == false && styles[i].fontStyle != SWT.NORMAL) {
+ if (styleCopy == null) {
+ styleCopy = (StyleRange) styles[i].clone();
+ }
+ styleCopy.fontStyle = SWT.NORMAL;
+ }
+ if (styleCopy != null) {
+ styles[i] = styleCopy;
+ }
+ }
+ lineStyles.put(new Integer(lineOffset), event);
+ }
+ }
+ /**
+ * Copies the text of the specified <class>StyledTextContent</class>.
+ * </p>
+ * @param original the <class>StyledTextContent</class> to copy.
+ */
+ void copyContent(StyledTextContent original) {
+ int insertOffset = 0;
+
+ printerContent = new DefaultContent();
+ for (int i = 0; i < original.getLineCount(); i++) {
+ int insertEndOffset;
+ if (i < original.getLineCount() - 1) {
+ insertEndOffset = original.getOffsetAtLine(i + 1);
+ }
+ else {
+ insertEndOffset = original.getCharCount();
+ }
+ printerContent.replaceTextRange(insertOffset, 0, original.getTextRange(insertOffset, insertEndOffset - insertOffset));
+ insertOffset = insertEndOffset;
+ }
+ }
+ /**
+ * Replaces all display colors in the cached line backgrounds and
+ * line styles with printer colors.
+ */
+ void createPrinterColors() {
+ Enumeration values = lineBackgrounds.elements();
+ printerColors = new Hashtable();
+ while (values.hasMoreElements()) {
+ StyledTextEvent event = (StyledTextEvent) values.nextElement();
+ event.lineBackground = getPrinterColor(event.lineBackground);
+ }
+
+ values = lineStyles.elements();
+ while (values.hasMoreElements()) {
+ StyledTextEvent event = (StyledTextEvent) values.nextElement();
+ for (int i = 0; i < event.styles.length; i++) {
+ StyleRange style = event.styles[i];
+ Color printerBackground = getPrinterColor(style.background);
+ Color printerForeground = getPrinterColor(style.foreground);
+
+ if (printerBackground != style.background ||
+ printerForeground != style.foreground) {
+ style = (StyleRange) style.clone();
+ style.background = printerBackground;
+ style.foreground = printerForeground;
+ event.styles[i] = style;
+ }
+ }
+ }
+ }
+ /**
+ * Disposes of the resources and the <class>PrintRenderer</class>.
+ */
+ void dispose() {
+ if (printerColors != null) {
+ Enumeration colors = printerColors.elements();
+
+ while (colors.hasMoreElements()) {
+ Color color = (Color) colors.nextElement();
+ color.dispose();
+ }
+ printerColors = null;
+ }
+ if (gc != null) {
+ gc.dispose();
+ gc = null;
+ }
+ if (printerFont != null) {
+ printerFont.dispose();
+ printerFont = null;
+ }
+ if (renderer != null) {
+ renderer.dispose();
+ renderer = null;
+ }
+ }
+ /**
+ * Finish printing the indicated page.
+ *
+ * @param page page that was printed
+ */
+ void endPage(int page) {
+ printDecoration(page, false);
+ printer.endPage();
+ }
+ /**
+ * Creates a <class>PrintRenderer</class> and calculate the line range
+ * to print.
+ */
+ void initializeRenderer() {
+ Rectangle trim = printer.computeTrim(0, 0, 0, 0);
+ Point dpi = printer.getDPI();
+
+ printerFont = new Font(printer, displayFontData.getName(), displayFontData.getHeight(), SWT.NORMAL);
+ clientArea = printer.getClientArea();
+ pageWidth = clientArea.width;
+ // one inch margin around text
+ clientArea.x = dpi.x + trim.x;
+ clientArea.y = dpi.y + trim.y;
+ clientArea.width -= (clientArea.x + trim.width);
+ clientArea.height -= (clientArea.y + trim.height);
+
+ gc = new GC(printer);
+ gc.setFont(printerFont);
+ renderer = new PrintRenderer(
+ printer, printerFont, isBidi(), gc, printerContent,
+ lineBackgrounds, lineStyles, bidiSegments,
+ tabLength, clientArea);
+ if (printOptions.header != null) {
+ int lineHeight = renderer.getLineHeight();
+ clientArea.y += lineHeight * 2;
+ clientArea.height -= lineHeight * 2;
+ }
+ if (printOptions.footer != null) {
+ clientArea.height -= renderer.getLineHeight() * 2;
+ }
+ pageSize = clientArea.height / renderer.getLineHeight();
+ StyledTextContent content = renderer.getContent();
+ startLine = 0;
+ endLine = content.getLineCount() - 1;
+ PrinterData data = printer.getPrinterData();
+ if (data.scope == PrinterData.PAGE_RANGE) {
+ startLine = (startPage - 1) * pageSize;
+ } else if (data.scope == PrinterData.SELECTION) {
+ startLine = content.getLineAtOffset(selection.x);
+ if (selection.y > 0) {
+ endLine = content.getLineAtOffset(selection.x + selection.y - 1);
+ } else {
+ endLine = startLine - 1;
+ }
+ }
+ }
+ /**
+ * Returns the printer color for the given display color.
+ * </p>
+ * @param color display color
+ * @return color create on the printer with the same RGB values
+ * as the display color.
+ */
+ Color getPrinterColor(Color color) {
+ Color printerColor = null;
+
+ if (color != null) {
+ printerColor = (Color) printerColors.get(color);
+ if (printerColor == null) {
+ printerColor = new Color(printer, color.getRGB());
+ printerColors.put(color, printerColor);
+ }
+ }
+ return printerColor;
+ }
+ /**
+ * Prints the lines in the specified page range.
+ */
+ void print() {
+ StyledTextContent content = renderer.getContent();
+ Color background = gc.getBackground();
+ Color foreground = gc.getForeground();
+ int lineHeight = renderer.getLineHeight();
+ int lineCount = content.getLineCount();
+ int paintY = clientArea.y;
+ int page = startPage;
+
+ if (singleLine) {
+ lineCount = 1;
+ }
+ for (int i = startLine; i <= endLine && page <= endPage; i++, paintY += lineHeight) {
+ String line = content.getLine(i);
+
+ if (paintY == clientArea.y) {
+ startPage(page);
+ }
+ renderer.drawLine(
+ line, i, paintY, gc, background, foreground, true);
+ if (paintY + lineHeight * 2 > clientArea.y + clientArea.height) {
+ endPage(page);
+ paintY = clientArea.y;
+ page++;
+ if (page > endPage || i == lineCount - 1) {
+ break;
+ }
+ }
+ }
+ if (paintY > clientArea.y && paintY <= clientArea.y + clientArea.height) {
+ endPage(page);
+ }
+ }
+ /**
+ * Print header or footer decorations.
+ * * @param page page number to print, if specified in the StyledTextPrintOptions header or footer. * @param header true = print the header, false = print the footer */
+ void printDecoration(int page, boolean header) {
+ int lastSegmentIndex = 0;
+ final int SegmentCount = 3;
+ String text;
+
+ if (header) {
+ text = printOptions.header;
+ }
+ else {
+ text = printOptions.footer;
+ }
+ if (text == null) {
+ return;
+ }
+ for (int i = 0; i < SegmentCount; i++) {
+ int segmentIndex = text.indexOf(StyledTextPrintOptions.SEPARATOR, lastSegmentIndex);
+ String segment;
+
+ if (segmentIndex == -1) {
+ segment = text.substring(lastSegmentIndex);
+ printDecorationSegment(segment, i, page, header);
+ break;
+ }
+ else {
+ segment = text.substring(lastSegmentIndex, segmentIndex);
+ printDecorationSegment(segment, i, page, header);
+ lastSegmentIndex = segmentIndex + StyledTextPrintOptions.SEPARATOR.length();
+ }
+ }
+ }
+ /**
+ * Print one segment of a header or footer decoration.
+ * Headers and footers have three different segments.
+ * One each for left aligned, centered, and right aligned text.
+ *
+ * @param segment decoration segment to print
+ * @param alignment alignment of the segment. 0=left, 1=center, 2=right
+ * @param page page number to print, if specified in the decoration segment.
+ * @param header true = print the header, false = print the footer
+ */
+ void printDecorationSegment(String segment, int alignment, int page, boolean header) {
+ int pageIndex = segment.indexOf(StyledTextPrintOptions.PAGE_TAG);
+
+ if (pageIndex != -1) {
+ final int PageTagLength = StyledTextPrintOptions.PAGE_TAG.length();
+ StringBuffer buffer = new StringBuffer(segment);
+ buffer.replace(pageIndex, pageIndex + PageTagLength, new Integer(page).toString());
+ segment = buffer.toString();
+ }
+ if (segment.length() > 0) {
+ int segmentWidth;
+ int drawX = 0;
+ int drawY;
+ StyledTextBidi bidi = null;
+
+ if (isBidi()) {
+ bidi = new StyledTextBidi(gc, tabLength, segment, null, null, new int[] {0, segment.length()});
+ segmentWidth = bidi.getTextWidth();
+ }
+ else {
+ segmentWidth = gc.textExtent(segment).x;
+ }
+ if (header) {
+ drawY = clientArea.y - renderer.getLineHeight() * 2;
+ }
+ else {
+ drawY = clientArea.y + clientArea.height + renderer.getLineHeight();
+ }
+ if (alignment == LEFT) {
+ drawX = clientArea.x;
+ }
+ else
+ if (alignment == CENTER) {
+ drawX = (pageWidth - segmentWidth) / 2;
+ }
+ else
+ if (alignment == RIGHT) {
+ drawX = clientArea.x + clientArea.width - segmentWidth;
+ }
+ if (bidi != null) {
+ bidi.drawBidiText(0, segment.length(), drawX, drawY);
+ }
+ else {
+ gc.drawString(segment, drawX, drawY, true);
+ }
+ }
+ }
+ /**
+ * Starts a print job and prints the pages specified in the constructor.
+ */
+ public void run() {
+ String jobName = printOptions.jobName;
+
+ if (jobName == null) {
+ jobName = "Printing";
+ }
+ if (printer.startJob(jobName)) {
+ createPrinterColors();
+ initializeRenderer();
+ print();
+ dispose();
+ printer.endJob();
+ }
+ }
+ /**
+ * Start printing a new page.
+ *
+ * @param page page number to be started
+ */
+ void startPage(int page) {
+ printer.startPage();
+ printDecoration(page, true);
+ }
+ }
+ /**
+ * The <code>RTFWriter</code> class is used to write widget content as
+ * rich text. The implementation complies with the RTF specification
+ * version 1.5.
+ * <p>
+ * toString() is guaranteed to return a valid RTF string only after
+ * close() has been called.
+ * </p>
+ * <p>
+ * Whole and partial lines and line breaks can be written. Lines will be
+ * formatted using the styles queried from the LineStyleListener, if
+ * set, or those set directly in the widget. All styles are applied to
+ * the RTF stream like they are rendered by the widget. In addition, the
+ * widget font name and size is used for the whole text.
+ * </p>
+ */
+ class RTFWriter extends TextWriter {
+ final int DEFAULT_FOREGROUND = 0;
+ final int DEFAULT_BACKGROUND = 1;
+ Vector colorTable = new Vector();
+ boolean WriteUnicode;
+
+ /**
+ * Creates a RTF writer that writes content starting at offset "start"
+ * in the document. <code>start</code> and <code>length</code>can be set to specify partial
+ * lines.
+ * <p>
+ *
+ * @param start start offset of content to write, 0 based from
+ * beginning of document
+ * @param length length of content to write
+ */
+ public RTFWriter(int start, int length) {
+ super(start, length);
+ colorTable.addElement(getForeground());
+ colorTable.addElement(getBackground());
+ setUnicode();
+ }
+ /**
+ * Closes the RTF writer. Once closed no more content can be written.
+ * <b>NOTE:</b> <code>toString()</code> does not return a valid RTF string until
+ * <code>close()</code> has been called.
+ */
+ public void close() {
+ if (isClosed() == false) {
+ writeHeader();
+ write("\n}}\0");
+ super.close();
+ }
+ }
+ /**
+ * Returns the index of the specified color in the RTF color table.
+ * <p>
+ *
+ * @param color the color
+ * @param defaultIndex return value if color is null
+ * @return the index of the specified color in the RTF color table
+ * or "defaultIndex" if "color" is null.
+ */
+ int getColorIndex(Color color, int defaultIndex) {
+ int index;
+
+ if (color == null) {
+ index = defaultIndex;
+ }
+ else {
+ index = colorTable.indexOf(color);
+ if (index == -1) {
+ index = colorTable.size();
+ colorTable.addElement(color);
+ }
+ }
+ return index;
+ }
+ /**
+ * Determines if Unicode RTF should be written.
+ * Don't write Unicode RTF on Windows 95/98/ME or NT.
+ */
+ void setUnicode() {
+ final String Win95 = "windows 95";
+ final String Win98 = "windows 98";
+ final String WinME = "windows me";
+ final String WinNT = "windows nt";
+ String osName = System.getProperty("os.name").toLowerCase();
+ String osVersion = System.getProperty("os.version");
+ int majorVersion = 0;
+
+ if (osName.startsWith(WinNT) && osVersion != null) {
+ int majorIndex = osVersion.indexOf('.');
+ if (majorIndex != -1) {
+ osVersion = osVersion.substring(0, majorIndex);
+ try {
+ majorVersion = Integer.parseInt(osVersion);
+ }
+ catch (NumberFormatException exception) {
+ // ignore exception. version number remains unknown.
+ // will write without Unicode
+ }
+ }
+ }
+ if (osName != null &&
+ osName.startsWith(Win95) == false &&
+ osName.startsWith(Win98) == false &&
+ osName.startsWith(WinME) == false &&
+ (osName.startsWith(WinNT) == false || majorVersion > 4)) {
+ WriteUnicode = true;
+ }
+ else {
+ WriteUnicode = false;
+ }
+ }
+ /**
+ * Appends the specified segment of "string" to the RTF data.
+ * Copy from <code>start</code> up to, but excluding, <code>end</code>.
+ * <p>
+ *
+ * @param string string to copy a segment from. Must not contain
+ * line breaks. Line breaks should be written using writeLineDelimiter()
+ * @param start start offset of segment. 0 based.
+ * @param end end offset of segment
+ */
+ void write(String string, int start, int end) {
+ for (int index = start; index < end; index++) {
+ char ch = string.charAt(index);
+ if (ch > 0xFF && WriteUnicode) {
+ // write the sub string from the last escaped character
+ // to the current one. Fixes bug 21698.
+ if (index > start) {
+ write(string.substring(start, index));
+ }
+ write("\\u");
+ write(Integer.toString((short) ch));
+ write(' '); // control word delimiter
+ start = index + 1;
+ }
+ else
+ if (ch == '}' || ch == '{' || ch == '\\') {
+ // write the sub string from the last escaped character
+ // to the current one. Fixes bug 21698.
+ if (index > start) {
+ write(string.substring(start, index));
+ }
+ write('\\');
+ write(ch);
+ start = index + 1;
+ }
+ }
+ // write from the last escaped character to the end.
+ // Fixes bug 21698.
+ if (start < end) {
+ write(string.substring(start, end));
+ }
+ }
+ /**
+ * Writes the RTF header including font table and color table.
+ */
+ void writeHeader() {
+ StringBuffer header = new StringBuffer();
+ FontData fontData = getFont().getFontData()[0];
+ header.append("{\\rtf1\\ansi");
+ // specify code page, necessary for copy to work in bidi
+ // systems that don't support Unicode RTF.
+ String cpg = System.getProperty("file.encoding").toLowerCase();
+ if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
+ cpg = cpg.substring(2, cpg.length());
+ header.append("\\ansicpg");
+ header.append(cpg);
+ }
+ header.append("\\uc0\\deff0{\\fonttbl{\\f0\\fnil ");
+ header.append(fontData.getName());
+ header.append(";}}\n{\\colortbl");
+ for (int i = 0; i < colorTable.size(); i++) {
+ Color color = (Color) colorTable.elementAt(i);
+ header.append("\\red");
+ header.append(color.getRed());
+ header.append("\\green");
+ header.append(color.getGreen());
+ header.append("\\blue");
+ header.append(color.getBlue());
+ header.append(";");
+ }
+ // some RTF readers ignore the deff0 font tag. Explicitly
+ // set the font for the whole document to work around this.
+ header.append("}\n{\\f0\\fs");
+ // font size is specified in half points
+ header.append(fontData.getHeight() * 2);
+ header.append(" ");
+ write(header.toString(), 0);
+ }
+ /**
+ * Appends the specified line text to the RTF data. Lines will be formatted
+ * using the styles queried from the LineStyleListener, if set, or those set
+ * directly in the widget.
+ * <p>
+ *
+ * @param line line text to write as RTF. Must not contain line breaks
+ * Line breaks should be written using writeLineDelimiter()
+ * @param lineOffset offset of the line. 0 based from the start of the
+ * widget document. Any text occurring before the start offset or after the
+ * end offset specified during object creation is ignored.
+ * @exception SWTException <ul>
+ * <li>ERROR_IO when the writer is closed.</li>
+ * </ul>
+ */
+ public void writeLine(String line, int lineOffset) {
+ StyleRange[] styles = new StyleRange[0];
+ Color lineBackground = null;
+ StyledTextEvent event;
+
+ if (isClosed()) {
+ SWT.error(SWT.ERROR_IO);
+ }
+ event = renderer.getLineStyleData(lineOffset, line);
+ if (event != null) {
+ styles = event.styles;
+ }
+ event = renderer.getLineBackgroundData(lineOffset, line);
+ if (event != null) {
+ lineBackground = event.lineBackground;
+ }
+ if (lineBackground == null) {
+ lineBackground = getBackground();
+ }
+ writeStyledLine(line, lineOffset, styles, lineBackground);
+ }
+ /**
+ * Appends the specified line delmimiter to the RTF data.
+ * <p>
+ *
+ * @param lineDelimiter line delimiter to write as RTF.
+ * @exception SWTException <ul>
+ * <li>ERROR_IO when the writer is closed.</li>
+ * </ul>
+ */
+ public void writeLineDelimiter(String lineDelimiter) {
+ if (isClosed()) {
+ SWT.error(SWT.ERROR_IO);
+ }
+ write(lineDelimiter, 0, lineDelimiter.length());
+ write("\\par ");
+ }
+ /**
+ * Appends the specified line text to the RTF data.
+ * Use the colors and font styles specified in "styles" and "lineBackground".
+ * Formatting is written to reflect the text rendering by the text widget.
+ * Style background colors take precedence over the line background color.
+ * Background colors are written using the \highlight tag (vs. the \cb tag).
+ * <p>
+ *
+ * @param line line text to write as RTF. Must not contain line breaks
+ * Line breaks should be written using writeLineDelimiter()
+ * @param lineOffset offset of the line. 0 based from the start of the
+ * widget document. Any text occurring before the start offset or after the
+ * end offset specified during object creation is ignored.
+ * @param styles styles to use for formatting. Must not be null.
+ * @param linebackground line background color to use for formatting.
+ * May be null.
+ */
+ void writeStyledLine(String line, int lineOffset, StyleRange[] styles, Color lineBackground) {
+ int lineLength = line.length();
+ int lineIndex;
+ int copyEnd;
+ int startOffset = getStart();
+ int endOffset = startOffset + super.getCharCount();
+ int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
+ int writeOffset = startOffset - lineOffset;
+
+ if (writeOffset >= line.length()) {
+ return; // whole line is outside write range
+ }
+ else
+ if (writeOffset > 0) {
+ lineIndex = writeOffset; // line starts before RTF write start
+ }
+ else {
+ lineIndex = 0;
+ }
+ if (lineBackground != null) {
+ write("{\\highlight");
+ write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
+ write(" ");
+ }
+ for (int i = 0; i < styles.length; i++) {
+ StyleRange style = styles[i];
+ int start = style.start - lineOffset;
+ int end = start + style.length;
+ int colorIndex;
+ // skip over partial first line
+ if (end < writeOffset) {
+ continue;
+ }
+ // style starts beyond line end or RTF write end
+ if (start >= lineEndOffset) {
+ break;
+ }
+ // write any unstyled text
+ if (lineIndex < start) {
+ // copy to start of style
+ // style starting betond end of write range or end of line
+ // is guarded against above.
+ write(line, lineIndex, start);
+ lineIndex = start;
+ }
+ // write styled text
+ colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
+ write("{\\cf");
+ write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
+ if (colorIndex != DEFAULT_BACKGROUND) {
+ write("\\highlight");
+ write(colorIndex);
+ }
+ if (style.fontStyle == SWT.BOLD) {
+ write("\\b");
+ }
+ write(" ");
+ // copy to end of style or end of write range or end of line
+ copyEnd = Math.min(end, lineEndOffset);
+ // guard against invalid styles and let style processing continue
+ copyEnd = Math.max(copyEnd, lineIndex);
+ write(line, lineIndex, copyEnd);
+ if (style.fontStyle == SWT.BOLD) {
+ write("\\b0");
+ }
+ write("}");
+ lineIndex = copyEnd;
+ }
+ // write unstyled text at the end of the line
+ if (lineIndex < lineEndOffset) {
+ write(line, lineIndex, lineEndOffset);
+ }
+ if (lineBackground != null) {
+ write("}");
+ }
+ }
+ }
+ /**
+ * The <code>TextWriter</code> class is used to write widget content to
+ * a string. Whole and partial lines and line breaks can be written. To write
+ * partial lines, specify the start and length of the desired segment
+ * during object creation.
+ * <p>
+ * </b>NOTE:</b> <code>toString()</code> is guaranteed to return a valid string only after close()
+ * has been called.
+ */
+ class TextWriter {
+ private StringBuffer buffer;
+ private int startOffset; // offset of first character that will be written
+ private int endOffset; // offset of last character that will be written.
+ // 0 based from the beginning of the widget text.
+ private boolean isClosed = false;
+
+ /**
+ * Creates a writer that writes content starting at offset "start"
+ * in the document. <code>start</code> and <code>length</code> can be set to specify partial lines.
+ * <p>
+ *
+ * @param start start offset of content to write, 0 based from beginning of document
+ * @param length length of content to write
+ */
+ public TextWriter(int start, int length) {
+ buffer = new StringBuffer(length);
+ startOffset = start;
+ endOffset = start + length;
+ }
+ /**
+ * Closes the writer. Once closed no more content can be written.
+ * <b>NOTE:</b> <code>toString()</code> is not guaranteed to return a valid string unless
+ * the writer is closed.
+ */
+ public void close() {
+ if (isClosed == false) {
+ isClosed = true;
+ }
+ }
+ /**
+ * Returns the number of characters to write.
+ */
+ public int getCharCount() {
+ return endOffset - startOffset;
+ }
+ /**
+ * Returns the offset where writing starts. 0 based from the start of
+ * the widget text. Used to write partial lines.
+ */
+ public int getStart() {
+ return startOffset;
+ }
+ /**
+ * Returns whether the writer is closed.
+ */
+ public boolean isClosed() {
+ return isClosed;
+ }
+ /**
+ * Returns the string. <code>close()</code> must be called before <code>toString()</code>
+ * is guaranteed to return a valid string.
+ * <p>
+ *
+ * @return the string
+ */
+ public String toString() {
+ return buffer.toString();
+ }
+ /**
+ * Appends the given string to the data.
+ */
+ void write(String string) {
+ buffer.append(string);
+ }
+ /**
+ * Inserts the given string to the data at the specified offset.
+ * Do nothing if "offset" is < 0 or > getCharCount()
+ * <p>
+ *
+ * @param string text to insert
+ * @param offset offset in the existing data to insert "string" at.
+ */
+ void write(String string, int offset) {
+ if (offset < 0 || offset > buffer.length()) {
+ return;
+ }
+ buffer.insert(offset, string);
+ }
+ /**
+ * Appends the given int to the data.
+ */
+ void write(int i) {
+ buffer.append(i);
+ }
+ /**
+ * Appends the given character to the data.
+ */
+ void write(char i) {
+ buffer.append(i);
+ }
+ /**
+ * Appends the specified line text to the data.
+ * <p>
+ *
+ * @param line line text to write. Must not contain line breaks
+ * Line breaks should be written using writeLineDelimiter()
+ * @param lineOffset offset of the line. 0 based from the start of the
+ * widget document. Any text occurring before the start offset or after the
+ * end offset specified during object creation is ignored.
+ * @exception SWTException <ul>
+ * <li>ERROR_IO when the writer is closed.</li>
+ * </ul>
+ */
+ public void writeLine(String line, int lineOffset) {
+ int lineLength = line.length();
+ int lineIndex;
+ int copyEnd;
+ int writeOffset = startOffset - lineOffset;
+
+ if (isClosed) {
+ SWT.error(SWT.ERROR_IO);
+ }
+ if (writeOffset >= lineLength) {
+ return; // whole line is outside write range
+ }
+ else
+ if (writeOffset > 0) {
+ lineIndex = writeOffset; // line starts before write start
+ }
+ else {
+ lineIndex = 0;
+ }
+ copyEnd = Math.min(lineLength, endOffset - lineOffset);
+ if (lineIndex < copyEnd) {
+ write(line.substring(lineIndex, copyEnd));
+ }
+ }
+ /**
+ * Appends the specified line delmimiter to the data.
+ * <p>
+ *
+ * @param lineDelimiter line delimiter to write
+ * @exception SWTException <ul>
+ * <li>ERROR_IO when the writer is closed.</li>
+ * </ul>
+ */
+ public void writeLineDelimiter(String lineDelimiter) {
+ if (isClosed) {
+ SWT.error(SWT.ERROR_IO);
+ }
+ write(lineDelimiter);
+ }
+ }
+ /**
+ * LineCache provides an interface to calculate and invalidate
+ * line based data.
+ * Implementors need to return a line width in <code>getWidth</code>.
+ */
+ interface LineCache {
+ /**
+ * Calculates the lines in the specified range.
+ * <p>
+ *
+ * @param startLine first line to calculate
+ * @param lineCount number of lines to calculate
+ */
+ public void calculate(int startLine, int lineCount);
+ /**
+ * Returns a width that will be used by the <code>StyledText</code>
+ * widget to size a horizontal scroll bar.
+ * <p>
+ *
+ * @return the line width
+ */
+ public int getWidth();
+ /**
+ * Resets the lines in the specified range.
+ * This method is called in <code>StyledText.redraw()</code>
+ * and allows implementors to call redraw themselves during reset.
+ * <p>
+ *
+ * @param startLine the first line to reset
+ * @param lineCount the number of lines to reset
+ * @param calculateMaxWidth true=implementors should retain a
+ * valid width even if it is affected by the reset operation.
+ * false=the width may be set to 0
+ */
+ public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth);
+ /**
+ * Resets the lines in the specified range.
+ * <p>
+ *
+ * @param startLine the first line to reset
+ * @param lineCount the number of lines to reset
+ * @param calculateMaxWidth true=implementors should retain a
+ * valid width even if it is affected by the reset operation.
+ * false=the width may be set to 0
+ */
+ public void reset(int startLine, int lineCount, boolean calculateMaxWidth);
+ /**
+ * Called when a text change occurred.
+ * <p>
+ *
+ * @param startOffset the start offset of the text change
+ * @param newLineCount the number of inserted lines
+ * @param replaceLineCount the number of deleted lines
+ * @param newCharCount the number of new characters
+ * @param replaceCharCount the number of deleted characters
+ */
+ public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount);
+ }
+ /**
+ * Keeps track of line widths and the longest line in the
+ * StyledText document.
+ * Line widths are calculated when requested by a call to
+ * <code>calculate</code> and cached until reset by a call
+ * to <code>redrawReset</code> or <code>reset</code>.
+ */
+ class ContentWidthCache implements LineCache {
+ StyledText parent; // parent widget, used to create a GC for line measuring
+ int[] lineWidth; // width in pixel of each line in the document, -1 for unknown width
+ int lineCount; // number of lines in lineWidth array
+ int maxWidth; // maximum line width of all measured lines
+ int maxWidthLineIndex; // index of the widest line
+
+ /**
+ * Creates a new <code>ContentWidthCache</code> and allocates space
+ * for the given number of lines.
+ * <p>
+ *
+ * @param parent the StyledText widget used to create a GC for
+ * line measuring
+ * @param lineCount initial number of lines to allocate space for
+ */
+ public ContentWidthCache(StyledText parent, int lineCount) {
+ this.lineCount = lineCount;
+ this.parent = parent;
+ lineWidth = new int[lineCount];
+ reset(0, lineCount, false);
+ }
+ /**
+ * Calculates the width of each line in the given range if it has
+ * not been calculated yet.
+ * If any line in the given range is wider than the currently widest
+ * line, the maximum line width is updated,
+ * <p>
+ *
+ * @param startLine first line to calculate the line width of
+ * @param lineCount number of lines to calculate the line width for
+ */
+ public void calculate(int startLine, int lineCount) {
+ GC gc = null;
+ int caretWidth = 0;
+ int stopLine = startLine + lineCount;
+
+ for (int i = startLine; i < stopLine; i++) {
+ if (lineWidth[i] == -1) {
+ String line = content.getLine(i);
+ int lineOffset = content.getOffsetAtLine(i);
+
+ if (gc == null) {
+ gc = parent.getGC();
+ caretWidth = getCaretWidth();
+ }
+ lineWidth[i] = contentWidth(line, lineOffset, gc) + caretWidth;
+ }
+ if (lineWidth[i] > maxWidth) {
+ maxWidth = lineWidth[i];
+ maxWidthLineIndex = i;
+ }
+ }
+ if (gc != null) {
+ gc.dispose();
+ }
+ }
+ /**
+ * Calculates the width of the visible lines in the specified
+ * range.
+ * <p>
+ *
+ * @param startLine the first changed line
+ * @param newLineCount the number of inserted lines
+ */
+ void calculateVisible(int startLine, int newLineCount) {
+ int topIndex = parent.getTopIndex();
+ int bottomLine = Math.min(getPartialBottomIndex(), startLine + newLineCount);
+
+ startLine = Math.max(startLine, topIndex);
+ calculate(startLine, bottomLine - startLine + 1);
+ }
+ /**
+ * Measures the width of the given line.
+ * <p>
+ *
+ * @param line the line to measure
+ * @param lineOffset start offset of the line to measure, relative
+ * to the start of the document
+ * @param gc the GC to use for measuring the line
+ * @param currentFont the font currently set in gc. Cached for better
+ * performance. Null when running in a bidi locale.
+ * @return the width of the given line
+ */
+ int contentWidth(String line, int lineOffset, GC gc) {
+ int width;
+
+ if (isBidi()) {
+ StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
+ width = bidi.getTextWidth();
+ }
+ else {
+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
+ StyleRange[] styles = null;
+ if (event != null) {
+ styles = renderer.filterLineStyles(event.styles);
+ }
+ width = renderer.getTextWidth(line, lineOffset, 0, line.length(), styles, 0, gc);
+ }
+ return width + leftMargin;
+ }
+ /**
+ * Grows the <code>lineWidth</code> array to accomodate new line width
+ * information.
+ * <p>
+ *
+ * @param numLines the number of elements to increase the array by
+ */
+ void expandLines(int numLines) {
+ int size = lineWidth.length;
+ if (size - lineCount >= numLines) {
+ return;
+ }
+ int[] newLines = new int[Math.max(size * 2, size + numLines)];
+ System.arraycopy(lineWidth, 0, newLines, 0, size);
+ lineWidth = newLines;
+ reset(size, lineWidth.length - size, false);
+ }
+ /**
+ * Returns the width of the longest measured line.
+ * <p>
+ *
+ * @return the width of the longest measured line.
+ */
+ public int getWidth() {
+ return maxWidth;
+ }
+ /**
+ * Updates the line width array to reflect inserted or deleted lines.
+ * <p>
+ *
+ * @param start the starting line of the change that took place
+ * @param delta the number of lines in the change, > 0 indicates lines inserted,
+ * < 0 indicates lines deleted
+ */
+ void linesChanged(int startLine, int delta) {
+ boolean inserting = delta > 0;
+
+ if (delta == 0) {
+ return;
+ }
+ if (inserting) {
+ // shift the lines down to make room for new lines
+ expandLines(delta);
+ for (int i = lineCount - 1; i >= startLine; i--) {
+ lineWidth[i + delta] = lineWidth[i];
+ }
+ // reset the new lines
+ for (int i = startLine + 1; i <= startLine + delta && i < lineWidth.length; i++) {
+ lineWidth[i] = -1;
+ }
+ // have new lines been inserted above the longest line?
+ if (maxWidthLineIndex >= startLine) {
+ maxWidthLineIndex += delta;
+ }
+ }
+ else {
+ // shift up the lines
+ for (int i = startLine - delta; i < lineCount; i++) {
+ lineWidth[i+delta] = lineWidth[i];
+ }
+ // has the longest line been removed?
+ if (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine - delta) {
+ maxWidth = 0;
+ maxWidthLineIndex = -1;
+ }
+ else
+ if (maxWidthLineIndex >= startLine - delta) {
+ maxWidthLineIndex += delta;
+ }
+ }
+ lineCount += delta;
+ }
+ /**
+ * Resets the line width of the lines in the specified range.
+ * <p>
+ *
+ * @param startLine the first line to reset
+ * @param lineCount the number of lines to reset
+ * @param calculateMaxWidth true=if the widest line is being
+ * reset the maximum width of all remaining cached lines is
+ * calculated. false=the maximum width is set to 0 if the
+ * widest line is being reset.
+ */
+ public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
+ reset(startLine, lineCount, calculateMaxWidth);
+ }
+ /**
+ * Resets the line width of the lines in the specified range.
+ * <p>
+ *
+ * @param startLine the first line to reset
+ * @param lineCount the number of lines to reset
+ * @param calculateMaxWidth true=if the widest line is being
+ * reset the maximum width of all remaining cached lines is
+ * calculated. false=the maximum width is set to 0 if the
+ * widest line is being reset.
+ */
+ public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
+ int endLine = startLine + lineCount;
+
+ if (startLine < 0 || endLine > lineWidth.length) {
+ return;
+ }
+ for (int i = startLine; i < endLine; i++) {
+ lineWidth[i] = -1;
+ }
+ // if the longest line is one of the reset lines, the maximum line
+ // width is no longer valid
+ if (maxWidthLineIndex >= startLine && maxWidthLineIndex < endLine) {
+ maxWidth = 0;
+ maxWidthLineIndex = -1;
+ if (calculateMaxWidth) {
+ for (int i = 0; i < lineCount; i++) {
+ if (lineWidth[i] > maxWidth) {
+ maxWidth = lineWidth[i];
+ maxWidthLineIndex = i;
+ }
+ }
+ }
+ }
+ }
+ /**
+ * Updates the line width array to reflect a text change.
+ * Lines affected by the text change will be reset.
+ * <p>
+ *
+ * @param startOffset the start offset of the text change
+ * @param newLineCount the number of inserted lines
+ * @param replaceLineCount the number of deleted lines
+ * @param newCharCount the number of new characters
+ * @param replaceCharCount the number of deleted characters
+ */
+ public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
+ int startLine = parent.getLineAtOffset(startOffset);
+ boolean removedMaxLine = (maxWidthLineIndex > startLine && maxWidthLineIndex <= startLine + replaceLineCount);
+ // entire text deleted?
+ if (startLine == 0 && replaceLineCount == lineCount) {
+ lineCount = newLineCount;
+ lineWidth = new int[lineCount];
+ reset(0, lineCount, false);
+ maxWidth = 0;
+ }
+ else {
+ linesChanged(startLine, -replaceLineCount);
+ linesChanged(startLine, newLineCount);
+ lineWidth[startLine] = -1;
+ }
+ // only calculate the visible lines. otherwise measurements of changed lines
+ // outside the visible area may subsequently change again without the
+ // lines ever being visible.
+ calculateVisible(startLine, newLineCount);
+ // maxWidthLineIndex will be -1 (i.e., unknown line width) if the widget has
+ // not been visible yet and the changed lines have therefore not been
+ // calculated above.
+ if (removedMaxLine ||
+ (maxWidthLineIndex != -1 && lineWidth[maxWidthLineIndex] < maxWidth)) {
+ // longest line has been removed or changed and is now shorter.
+ // need to recalculate maximum content width for all lines
+ maxWidth = 0;
+ for (int i = 0; i < lineCount; i++) {
+ if (lineWidth[i] > maxWidth) {
+ maxWidth = lineWidth[i];
+ maxWidthLineIndex = i;
+ }
+ }
+ }
+ }
+ }
+ /**
+ * Updates the line wrapping of the content.
+ * The line wrapping must always be in a consistent state.
+ * Therefore, when <code>reset</code> or <code>redrawReset</code>
+ * is called, the line wrapping is recalculated immediately
+ * instead of in <code>calculate</code>.
+ */
+ class WordWrapCache implements LineCache {
+ StyledText parent;
+ WrappedContent visualContent;
+
+ /**
+ * Creates a new <code>WordWrapCache</code> and calculates an initial
+ * line wrapping.
+ * <p>
+ *
+ * @param parent the StyledText widget to wrap content in.
+ * @param content the content provider that does the actual line wrapping.
+ */
+ public WordWrapCache(StyledText parent, WrappedContent content) {
+ this.parent = parent;
+ visualContent = content;
+ visualContent.wrapLines();
+ }
+ /**
+ * Do nothing. Lines are wrapped immediately after reset.
+ * <p>
+ *
+ * @param startLine first line to calculate
+ * @param lineCount number of lines to calculate
+ */
+ public void calculate(int startLine, int lineCount) {
+ }
+ /**
+ * Returns the client area width. Lines are wrapped so there
+ * is no horizontal scroll bar.
+ * <p>
+ *
+ * @return the line width
+ */
+ public int getWidth() {
+ return parent.getClientArea().width;
+ }
+ /**
+ * Wraps the lines in the specified range.
+ * This method is called in <code>StyledText.redraw()</code>.
+ * A redraw is therefore not necessary.
+ * <p>
+ *
+ * @param startLine the first line to reset
+ * @param lineCount the number of lines to reset
+ * @param calculateMaxWidth true=implementors should retain a
+ * valid width even if it is affected by the reset operation.
+ * false=the width may be set to 0
+ */
+ public void redrawReset(int startLine, int lineCount, boolean calculateMaxWidth) {
+ if (lineCount == visualContent.getLineCount()) {
+ // do a full rewrap if all lines are reset
+ visualContent.wrapLines();
+ }
+ else {
+ visualContent.reset(startLine, lineCount);
+ }
+ }
+ /**
+ * Rewraps the lines in the specified range and redraws
+ * the widget if the line wrapping has changed.
+ * <p>
+ *
+ * @param startLine the first line to reset
+ * @param lineCount the number of lines to reset
+ * @param calculateMaxWidth true=implementors should retain a
+ * valid width even if it is affected by the reset operation.
+ * false=the width may be set to 0
+ */
+ public void reset(int startLine, int lineCount, boolean calculateMaxWidth) {
+ int itemCount = getPartialBottomIndex() - topIndex + 1;
+ int[] oldLineOffsets = new int[itemCount];
+
+ for (int i = 0; i < itemCount; i++) {
+ oldLineOffsets[i] = visualContent.getOffsetAtLine(i + topIndex);
+ }
+ redrawReset(startLine, lineCount, calculateMaxWidth);
+ // check for cases which will require a full redraw
+ if (getPartialBottomIndex() - topIndex + 1 != itemCount) {
+ // number of visible lines has changed
+ parent.internalRedraw();
+ }
+ else {
+ for (int i = 0; i < itemCount; i++) {
+ if (visualContent.getOffsetAtLine(i + topIndex) != oldLineOffsets[i]) {
+ // wrapping of one of the visible lines has changed
+ parent.internalRedraw();
+ break;
+ }
+ }
+ }
+ }
+ /**
+ * Passes the text change notification to the line wrap content.
+ * <p>
+ *
+ * @param startOffset the start offset of the text change
+ * @param newLineCount the number of inserted lines
+ * @param replaceLineCount the number of deleted lines
+ * @param newCharCount the number of new characters
+ * @param replaceCharCount the number of deleted characters
+ */
+ public void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
+ int startLine = visualContent.getLineAtOffset(startOffset);
+
+ visualContent.textChanged(startOffset, newLineCount, replaceLineCount, newCharCount, replaceCharCount);
+ if (startLine <= getPartialBottomIndex()) {
+ // only redraw if the text change is inside or above the
+ // visible lines. if it is below the visible lines it will
+ // not affect the word wrapping. fixes bug 14047.
+ parent.internalRedraw();
+ }
+ }
+ }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#FULL_SELECTION
+ * @see SWT#MULTI
+ * @see SWT#READ_ONLY
+ * @see SWT#SINGLE
+ * @see SWT#WRAP
+ * @see #getStyle
+ */
+public StyledText(Composite parent, int style) {
+ super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND));
+ // set the bg/fg in the OS to ensure that these are the same as StyledText, necessary
+ // for ensuring that the bg/fg the IME box uses is the same as what StyledText uses
+ super.setForeground(getForeground());
+ super.setBackground(getBackground());
+ Display display = getDisplay();
+ isBidi = StyledTextBidi.isBidiPlatform();
+ if ((style & SWT.READ_ONLY) != 0) {
+ setEditable(false);
+ }
+ if ((style & SWT.BORDER) == 0 || (style & SWT.SINGLE) == 0) {
+ leftMargin = topMargin = rightMargin = bottomMargin = 0;
+ }
+ clipboard = new Clipboard(display);
+ installDefaultContent();
+ initializeRenderer();
+ if ((style & SWT.WRAP) != 0) {
+ setWordWrap(true);
+ }
+ else {
+ lineCache = new ContentWidthCache(this, content.getLineCount());
+ }
+ if (isBidi() == false) {
+ Caret caret = new Caret(this, SWT.NULL);
+ caret.setSize(1, caret.getSize().y);
+ }
+ else {
+ createCaretBitmaps();
+ new Caret(this, SWT.NULL);
+ setBidiCaretDirection();
+ Runnable runnable = new Runnable() {
+ public void run() {
+ // setBidiCaretLocation calculates caret location like during
+ // cursor movement and takes keyboard language into account.
+ // Fixes 1GKPYMK
+ setBidiCaretLocation(null);
+ }
+ };
+ StyledTextBidi.addLanguageListener(this, runnable);
+ }
+
+ String platform= SWT.getPlatform();
+ isCarbon = "carbon".equals(platform);
+
+ // set the caret width, the height of the caret will default to the line height
+ calculateScrollBars();
+ createKeyBindings();
+ ibeamCursor = new Cursor(display, SWT.CURSOR_IBEAM);
+ setCursor(ibeamCursor);
+ installListeners();
+ installDefaultLineStyler();
+}
+/**
+ * Adds an extended modify listener. An ExtendedModify event is sent by the
+ * widget when the widget text has changed.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
+ checkWidget();
+ if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ StyledTextListener typedListener = new StyledTextListener(extendedModifyListener);
+ addListener(ExtendedModify, typedListener);
+}
+/**
+ * Maps a key to an action.
+ * One action can be associated with N keys. However, each key can only
+ * have one action (key:action is N:1 relation).
+ * <p>
+ *
+ * @param key a key code defined in SWT.java or a character.
+ * Optionally ORd with a state mask. Preferred state masks are one or more of
+ * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
+ * differences. However, there may be cases where using the specific state masks
+ * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
+ * @param action one of the predefined actions defined in ST.java.
+ * Use SWT.NULL to remove a key binding.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setKeyBinding(int key, int action) {
+ checkWidget();
+
+ int keyValue = key & SWT.KEY_MASK;
+ int modifierValue = key & SWT.MODIFIER_MASK;
+ char keyChar = (char)keyValue;
+
+ if (Character.isLetter(keyChar)) {
+ // make the keybinding case insensitive by adding it
+ // in its upper and lower case form
+ char ch = Character.toUpperCase(keyChar);
+ int newKey = ch | modifierValue;
+ if (action == SWT.NULL) {
+ keyActionMap.remove(new Integer(newKey));
+ }
+ else {
+ keyActionMap.put(new Integer(newKey), new Integer(action));
+ }
+ ch = Character.toLowerCase(keyChar);
+ newKey = ch | modifierValue;
+ if (action == SWT.NULL) {
+ keyActionMap.remove(new Integer(newKey));
+ }
+ else {
+ keyActionMap.put(new Integer(newKey), new Integer(action));
+ }
+ } else {
+ if (action == SWT.NULL) {
+ keyActionMap.remove(new Integer(key));
+ }
+ else {
+ keyActionMap.put(new Integer(key), new Integer(action));
+ }
+ }
+
+}
+/**
+ * Adds a bidirectional segment listener. A BidiSegmentEvent is sent
+ * whenever a line of text is measured or rendered. The user can
+ * specify text ranges in the line that should be treated as if they
+ * 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
+ * should generally remain in logical order to each other, that is, the
+ * way they are stored.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ * @see BidiSegmentEvent
+ * @since 2.0
+ */
+public void addBidiSegmentListener(BidiSegmentListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ StyledTextListener typedListener = new StyledTextListener(listener);
+ addListener(LineGetSegments, typedListener);
+}
+/**
+ * Adds a line background listener. A LineGetBackground event is sent by the
+ * widget to determine the background color for a line.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addLineBackgroundListener(LineBackgroundListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ if (userLineBackground == false) {
+ removeLineBackgroundListener(defaultLineStyler);
+ defaultLineStyler.setLineBackground(0, logicalContent.getLineCount(), null);
+ userLineBackground = true;
+ }
+ StyledTextListener typedListener = new StyledTextListener(listener);
+ addListener(LineGetBackground, typedListener);
+}
+/**
+ * Adds a line style listener. A LineGetStyle event is sent by the widget to
+ * determine the styles for a line.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addLineStyleListener(LineStyleListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ if (userLineStyle == false) {
+ removeLineStyleListener(defaultLineStyler);
+ defaultLineStyler.setStyleRange(null);
+ userLineStyle = true;
+ }
+ StyledTextListener typedListener = new StyledTextListener(listener);
+ addListener(LineGetStyle, typedListener);
+}
+/**
+ * Adds a modify listener. A Modify event is sent by the widget when the widget text
+ * has changed.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addModifyListener(ModifyListener modifyListener) {
+ checkWidget();
+ if (modifyListener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(modifyListener);
+ addListener(SWT.Modify, typedListener);
+}
+/**
+ * Adds a selection listener. A Selection event is sent by the widget when the
+ * selection has changed.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+}
+/**
+ * Adds a verify key listener. A VerifyKey event is sent by the widget when a key
+ * is pressed. The widget ignores the key press if the listener sets the doit field
+ * of the event to false.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addVerifyKeyListener(VerifyKeyListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ StyledTextListener typedListener = new StyledTextListener(listener);
+ addListener(VerifyKey, typedListener);
+}
+/**
+ * Adds a verify listener. A Verify event is sent by the widget when the widget text
+ * is about to change. The listener can set the event text and the doit field to
+ * change the text that is set in the widget or to force the widget to ignore the
+ * text change.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addVerifyListener(VerifyListener verifyListener) {
+ checkWidget();
+ if (verifyListener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(verifyListener);
+ addListener(SWT.Verify, typedListener);
+}
+/**
+ * Appends a string to the text at the end of the widget.
+ * <p>
+ *
+ * @param string the string to be appended
+ * @see #replaceTextRange(int,int,String)
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void append(String string) {
+ checkWidget();
+ if (string == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ int lastChar = Math.max(getCharCount(), 0);
+ replaceTextRange(lastChar, 0, string);
+}
+/**
+ * Calculates the width of the widest visible line.
+ */
+void calculateContentWidth() {
+ if (lineHeight != 0) {
+ lineCache = getLineCache(content);
+ lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
+ }
+}
+/**
+ * Calculates the scroll bars
+ */
+void calculateScrollBars() {
+ ScrollBar horizontalBar = getHorizontalBar();
+ ScrollBar verticalBar = getVerticalBar();
+
+ setScrollBars();
+ if (verticalBar != null) {
+ verticalBar.setIncrement(getVerticalIncrement());
+ }
+ if (horizontalBar != null) {
+ horizontalBar.setIncrement(getHorizontalIncrement());
+ }
+}
+/**
+ * Calculates the top index based on the current vertical scroll offset.
+ * The top index is the index of the topmost fully visible line or the
+ * topmost partially visible line if no line is fully visible.
+ * The top index starts at 0.
+ */
+void calculateTopIndex() {
+ int oldTopIndex = topIndex;
+ int verticalIncrement = getVerticalIncrement();
+ int clientAreaHeight = getClientArea().height;
+
+ if (verticalIncrement == 0) {
+ return;
+ }
+ topIndex = Compatibility.ceil(verticalScrollOffset, verticalIncrement);
+ // Set top index to partially visible top line if no line is fully
+ // visible but at least some of the widget client area is visible.
+ // Fixes bug 15088.
+ if (topIndex > 0) {
+ if (clientAreaHeight > 0) {
+ int bottomPixel = verticalScrollOffset + clientAreaHeight;
+ int fullLineTopPixel = topIndex * verticalIncrement;
+ int fullLineVisibleHeight = bottomPixel - fullLineTopPixel;
+ // set top index to partially visible line if no line fully fits in
+ // client area or if space is available but not used (the latter should
+ // never happen because we use claimBottomFreeSpace)
+ if (fullLineVisibleHeight < verticalIncrement) {
+ topIndex--;
+ }
+ }
+ else
+ if (topIndex >= content.getLineCount()) {
+ topIndex = content.getLineCount() - 1;
+ }
+ }
+ if (topIndex != oldTopIndex) {
+ topOffset = content.getOffsetAtLine(topIndex);
+ lineCache.calculate(topIndex, getPartialBottomIndex() - topIndex + 1);
+ setHorizontalScrollBar();
+ }
+}
+/**
+ * Hides the scroll bars if widget is created in single line mode.
+ */
+static int checkStyle(int style) {
+ if ((style & SWT.SINGLE) != 0) {
+ style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
+ }
+ return style;
+}
+/**
+ * Scrolls down the text to use new space made available by a resize or by
+ * deleted lines.
+ */
+void claimBottomFreeSpace() {
+ int newVerticalOffset = Math.max(0, content.getLineCount() * lineHeight - getClientArea().height);
+
+ if (newVerticalOffset < verticalScrollOffset) {
+ // Scroll up so that empty lines below last text line are used.
+ // Fixes 1GEYJM0
+ setVerticalScrollOffset(newVerticalOffset, true);
+ }
+}
+/**
+ * Scrolls text to the right to use new space made available by a resize.
+ */
+void claimRightFreeSpace() {
+ int newHorizontalOffset = Math.max(0, lineCache.getWidth() - (getClientArea().width - leftMargin - rightMargin));
+
+ if (newHorizontalOffset < horizontalScrollOffset) {
+ // item is no longer drawn past the right border of the client area
+ // align the right end of the item with the right border of the
+ // client area (window is scrolled right).
+ scrollHorizontalBar(newHorizontalOffset - horizontalScrollOffset);
+ }
+}
+/**
+ * Clears the widget margin.
+ *
+ * @param gc GC to render on
+ * @param background background color to use for clearing the margin
+ * @param clientArea widget client area dimensions
+ * @param renderHeight height in pixel of the rendered lines
+ */
+void clearMargin(GC gc, Color background, Rectangle clientArea, int renderHeight) {
+ if (renderHeight + topMargin <= 0) {
+ return;
+ }
+ // clear the margin background
+ gc.setBackground(background);
+ gc.fillRectangle(0, 0, clientArea.width, topMargin);
+ gc.fillRectangle(0, 0, leftMargin, renderHeight + topMargin);
+ gc.fillRectangle(
+ 0, clientArea.height - bottomMargin,
+ clientArea.width, bottomMargin);
+ gc.fillRectangle(
+ clientArea.width - rightMargin, 0,
+ rightMargin, renderHeight + topMargin);
+}
+/**
+ * Removes the widget selection.
+ * <p>
+ *
+ * @param sendEvent a Selection event is sent when set to true and when the selection is actually reset.
+ */
+void clearSelection(boolean sendEvent) {
+ int selectionStart = selection.x;
+ int selectionEnd = selection.y;
+ int length = content.getCharCount();
+
+ resetSelection();
+ // redraw old selection, if any
+ if (selectionEnd - selectionStart > 0) {
+ // called internally to remove selection after text is removed
+ // therefore make sure redraw range is valid.
+ int redrawStart = Math.min(selectionStart, length);
+ int redrawEnd = Math.min(selectionEnd, length);
+ if (redrawEnd - redrawStart > 0) {
+ internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
+ }
+ if (sendEvent == true) {
+ sendSelectionEvent();
+ }
+ }
+}
+/**
+ * Computes the preferred size.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget();
+ int count, width, height;
+ boolean singleLine = (getStyle() & SWT.SINGLE) != 0;
+
+ if (singleLine) {
+ count = 1;
+ } else {
+ count = content.getLineCount();
+ }
+ if (wHint != SWT.DEFAULT) {
+ width = wHint;
+ }
+ else {
+ width = DEFAULT_WIDTH;
+ }
+
+ if (wordWrap) {
+ if (((WrappedContent) content).getVisualLineCount() != 0) {
+ // lines have already been wrapped to a specific width.
+ // use existing line count. fixes bug 9191
+ if (wHint == SWT.DEFAULT) {
+ width = lineCache.getWidth();
+ } else {
+ ((WrappedContent) content).wrapLines(width);
+ // caret may be on a different line after a rewrap
+ setCaretLocation();
+ }
+ if (singleLine == false) {
+ count = content.getLineCount();
+ }
+ }
+ else {
+ if (singleLine == false) {
+ ((WrappedContent) content).wrapLines(width);
+ // caret may be on a different line after a rewrap
+ setCaretLocation();
+ count = content.getLineCount();
+ }
+ }
+ }
+ else if (wHint == SWT.DEFAULT) {
+ // Only calculate what can actually be displayed.
+ // Do this because measuring each text line is a
+ // time-consuming process.
+ int visibleCount = Math.min (count, getDisplay().getBounds().height / lineHeight);
+ lineCache.calculate(0, visibleCount);
+ width = lineCache.getWidth() + leftMargin + rightMargin;
+ }
+ if (hHint != SWT.DEFAULT) {
+ height = hHint;
+ }
+ else {
+ height = count * lineHeight + topMargin + bottomMargin;
+ }
+ // Use default values if no text is defined.
+ if (width == 0) {
+ width = DEFAULT_WIDTH;
+ }
+ if (height == 0) {
+ if (singleLine) {
+ height = lineHeight;
+ }
+ else {
+ height = DEFAULT_HEIGHT;
+ }
+ }
+ Rectangle rect = computeTrim(0, 0, width, height);
+ return new Point (rect.width, rect.height);
+}
+/**
+ * Copies the selected text to the clipboard. The text will be put in the
+ * clipboard in plain text format and RTF format.
+ * <p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void copy() {
+ checkWidget();
+ int length = selection.y - selection.x;
+ if (length > 0) {
+ try {
+ setClipboardContent(selection.x, length);
+ }
+ catch (SWTError error) {
+ // Copy to clipboard failed. This happens when another application
+ // is accessing the clipboard while we copy. Ignore the error.
+ // Fixes 1GDQAVN
+ // Rethrow all other errors. Fixes bug 17578.
+ if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
+ throw error;
+ }
+ }
+ }
+}
+/**
+ * Returns a string that uses only the line delimiter specified by the
+ * StyledTextContent implementation.
+ * Returns only the first line if the widget has the SWT.SINGLE style.
+ * <p>
+ *
+ * @param text the text that may have line delimiters that don't
+ * match the model line delimiter. Possible line delimiters
+ * are CR ('\r'), LF ('\n'), CR/LF ("\r\n")
+ * @return the converted text that only uses the line delimiter
+ * specified by the model. Returns only the first line if the widget
+ * has the SWT.SINGLE style.
+ */
+String getModelDelimitedText(String text) {
+ StringBuffer convertedText;
+ String delimiter = getLineDelimiter();
+ int length = text.length();
+ int crIndex = 0;
+ int lfIndex = 0;
+ int i = 0;
+
+ if (length == 0) {
+ return text;
+ }
+ convertedText = new StringBuffer(length);
+ while (i < length) {
+ if (crIndex != -1) {
+ crIndex = text.indexOf(SWT.CR, i);
+ }
+ if (lfIndex != -1) {
+ lfIndex = text.indexOf(SWT.LF, i);
+ }
+ if (lfIndex == -1 && crIndex == -1) { // no more line breaks?
+ break;
+ }
+ else // CR occurs before LF or no LF present?
+ if ((crIndex < lfIndex && crIndex != -1) || lfIndex == -1) {
+ convertedText.append(text.substring(i, crIndex));
+ if (lfIndex == crIndex + 1) { // CR/LF combination?
+ i = lfIndex + 1;
+ }
+ else {
+ i = crIndex + 1;
+ }
+ }
+ else { // LF occurs before CR!
+ convertedText.append(text.substring(i, lfIndex));
+ i = lfIndex + 1;
+ }
+ if (isSingleLine()) {
+ break;
+ }
+ convertedText.append(delimiter);
+ }
+ // copy remaining text if any and if not in single line mode or no
+ // text copied thus far (because there only is one line)
+ if (i < length && (isSingleLine() == false || convertedText.length() == 0)) {
+ convertedText.append(text.substring(i));
+ }
+ return convertedText.toString();
+}
+/**
+ * Creates default key bindings.
+ */
+void createKeyBindings() {
+ // Navigation
+ setKeyBinding(SWT.ARROW_UP, ST.LINE_UP);
+ setKeyBinding(SWT.ARROW_DOWN, ST.LINE_DOWN);
+ setKeyBinding(SWT.HOME, ST.LINE_START);
+ setKeyBinding(SWT.END, ST.LINE_END);
+ setKeyBinding(SWT.ARROW_LEFT, ST.COLUMN_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT, ST.COLUMN_NEXT);
+ setKeyBinding(SWT.PAGE_UP, ST.PAGE_UP);
+ setKeyBinding(SWT.PAGE_DOWN, ST.PAGE_DOWN);
+ setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1, ST.WORD_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1, ST.WORD_NEXT);
+ setKeyBinding(SWT.HOME | SWT.MOD1, ST.TEXT_START);
+ setKeyBinding(SWT.END | SWT.MOD1, ST.TEXT_END);
+ setKeyBinding(SWT.PAGE_UP | SWT.MOD1, ST.WINDOW_START);
+ setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1, ST.WINDOW_END);
+ // Selection
+ setKeyBinding(SWT.ARROW_UP | SWT.MOD2, ST.SELECT_LINE_UP);
+ setKeyBinding(SWT.ARROW_DOWN | SWT.MOD2, ST.SELECT_LINE_DOWN);
+ setKeyBinding(SWT.HOME | SWT.MOD2, ST.SELECT_LINE_START);
+ setKeyBinding(SWT.END | SWT.MOD2, ST.SELECT_LINE_END);
+ setKeyBinding(SWT.ARROW_LEFT | SWT.MOD2, ST.SELECT_COLUMN_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD2, ST.SELECT_COLUMN_NEXT);
+ setKeyBinding(SWT.PAGE_UP | SWT.MOD2, ST.SELECT_PAGE_UP);
+ setKeyBinding(SWT.PAGE_DOWN | SWT.MOD2, ST.SELECT_PAGE_DOWN);
+ setKeyBinding(SWT.ARROW_LEFT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT | SWT.MOD1 | SWT.MOD2, ST.SELECT_WORD_NEXT);
+ setKeyBinding(SWT.HOME | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_START);
+ setKeyBinding(SWT.END | SWT.MOD1 | SWT.MOD2, ST.SELECT_TEXT_END);
+ setKeyBinding(SWT.PAGE_UP | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_START);
+ setKeyBinding(SWT.PAGE_DOWN | SWT.MOD1 | SWT.MOD2, ST.SELECT_WINDOW_END);
+
+ // Modification
+ // Cut, Copy, Paste
+ setKeyBinding('X' | SWT.MOD1, ST.CUT);
+ setKeyBinding('C' | SWT.MOD1, ST.COPY);
+ setKeyBinding('V' | SWT.MOD1, ST.PASTE);
+ // Cut, Copy, Paste Wordstar style
+ setKeyBinding(SWT.DEL | SWT.MOD2, ST.CUT);
+ setKeyBinding(SWT.INSERT | SWT.MOD1, ST.COPY);
+ setKeyBinding(SWT.INSERT | SWT.MOD2, ST.PASTE);
+ setKeyBinding(SWT.BS | SWT.MOD2, ST.DELETE_PREVIOUS);
+
+ setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
+ setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
+ setKeyBinding(SWT.BS | SWT.MOD1, ST.DELETE_WORD_PREVIOUS);
+ setKeyBinding(SWT.DEL | SWT.MOD1, ST.DELETE_WORD_NEXT);
+
+ // Miscellaneous
+ setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
+}
+/**
+ * Create the bitmaps to use for the caret in bidi mode. This
+ * method only needs to be called upon widget creation and when the
+ * font changes (the caret bitmap height needs to match font height).
+ */
+void createCaretBitmaps() {
+ int caretWidth = BIDI_CARET_WIDTH;
+
+ Display display = getDisplay();
+ if (caretPalette == null) {
+ caretPalette = new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
+ }
+ if (leftCaretBitmap != null) {
+ leftCaretBitmap.dispose();
+ }
+ ImageData imageData = new ImageData(caretWidth, lineHeight, 1, caretPalette);
+ leftCaretBitmap = new Image(display, imageData);
+ GC gc = new GC (leftCaretBitmap);
+ gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
+ gc.drawLine(0,0,0,lineHeight);
+ gc.drawLine(0,0,caretWidth-1,0);
+ gc.drawLine(0,1,1,1);
+ gc.dispose();
+
+ if (rightCaretBitmap != null) {
+ rightCaretBitmap.dispose();
+ }
+ rightCaretBitmap = new Image(display, imageData);
+ gc = new GC (rightCaretBitmap);
+ gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
+ gc.drawLine(caretWidth-1,0,caretWidth-1,lineHeight);
+ gc.drawLine(0,0,caretWidth-1,0);
+ gc.drawLine(caretWidth-1,1,1,1);
+ gc.dispose();
+}
+/**
+ * Moves the selected text to the clipboard. The text will be put in the
+ * clipboard in plain text format and RTF format.
+ * <p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void cut(){
+ checkWidget();
+ int length = selection.y - selection.x;
+
+ if (length > 0) {
+ try {
+ setClipboardContent(selection.x, length);
+ }
+ catch (SWTError error) {
+ // Copy to clipboard failed. This happens when another application
+ // is accessing the clipboard while we copy. Ignore the error.
+ // Fixes 1GDQAVN
+ // Rethrow all other errors. Fixes bug 17578.
+ if (error.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
+ throw error;
+ }
+ // Abort cut operation if copy to clipboard fails.
+ // Fixes bug 21030.
+ return;
+ }
+ doDelete();
+ }
+}
+/**
+ * A mouse move event has occurred. See if we should start autoscrolling. If
+ * the move position is outside of the client area, initiate autoscrolling.
+ * Otherwise, we've moved back into the widget so end autoscrolling.
+ */
+void doAutoScroll(Event event) {
+ Rectangle area = getClientArea();
+
+ if (event.y > area.height) {
+ doAutoScroll(SWT.DOWN);
+ }
+ else
+ if (event.y < 0) {
+ doAutoScroll(SWT.UP);
+ }
+ else
+ if (event.x < leftMargin && wordWrap == false) {
+ doAutoScroll(SWT.LEFT);
+ }
+ else
+ if (event.x > area.width - leftMargin - rightMargin && wordWrap == false) {
+ doAutoScroll(SWT.RIGHT);
+ }
+ else {
+ endAutoScroll();
+ }
+}
+/**
+ * Initiates autoscrolling.
+ * <p>
+ *
+ * @param direction SWT.UP, SWT.DOWN, SWT.RIGHT, SWT.LEFT
+ */
+void doAutoScroll(int direction) {
+ Runnable timer = null;
+ final int TIMER_INTERVAL = 5;
+
+ // If we're already autoscrolling in the given direction do nothing
+ if (autoScrollDirection == direction) {
+ return;
+ }
+
+ final Display display = getDisplay();
+ // Set a timer that will simulate the user pressing and holding
+ // down a cursor key (i.e., arrowUp, arrowDown).
+ if (direction == SWT.UP) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.UP) {
+ doSelectionLineUp();
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ } else if (direction == SWT.DOWN) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.DOWN) {
+ doSelectionLineDown();
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ } else if (direction == SWT.RIGHT) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.RIGHT) {
+ doColumnRight();
+ setMouseWordSelectionAnchor();
+ doSelection(SWT.RIGHT);
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ } else if (direction == SWT.LEFT) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.LEFT) {
+ doColumnLeft();
+ setMouseWordSelectionAnchor();
+ doSelection(SWT.LEFT);
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ }
+ if (timer != null) {
+ autoScrollDirection = direction;
+ display.timerExec(TIMER_INTERVAL, timer);
+ }
+}
+/**
+ * Deletes the previous character. Delete the selected text if any.
+ * Move the caret in front of the deleted text.
+ */
+void doBackspace() {
+ Event event = new Event();
+ event.text = "";
+ if (selection.x != selection.y) {
+ event.start = selection.x;
+ event.end = selection.y;
+ sendKeyEvent(event);
+ }
+ else
+ if (caretOffset > 0) {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+
+ if (caretOffset == lineOffset) {
+ lineOffset = content.getOffsetAtLine(line - 1);
+ event.start = lineOffset + content.getLine(line - 1).length();
+ event.end = caretOffset;
+ }
+ else {
+ event.start = caretOffset - 1;
+ event.end = caretOffset;
+ }
+ sendKeyEvent(event);
+ }
+}
+/**
+ * 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
+ * left (visually left because it's now in a L2R segment).
+ */
+void doColumnLeft() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (isBidi()) {
+ String lineText = content.getLine(line);
+ int lineLength = lineText.length();
+ GC gc = getGC();
+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
+
+ if (horizontalScrollOffset > 0 || offsetInLine > 0) {
+ if (offsetInLine < lineLength && bidi.isRightToLeft(offsetInLine)) {
+ // advance caret logically if in R2L segment (move visually left)
+ caretOffset++;
+ doSelection(SWT.RIGHT);
+ if (caretOffset - lineOffset == lineLength) {
+ // if the line end is reached in a R2L segment, make the
+ // caret position (visual left border) visible before
+ // jumping to segment start
+ showCaret();
+ }
+ // end of R2L segment reached (visual left side)?
+ if (bidi.isRightToLeft(caretOffset - lineOffset) == false) {
+ if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
+ // make beginning of R2L segment visible before going
+ // left, to L2R segment important if R2L segment ends
+ // at visual left in order to scroll all the way to the
+ // left. Fixes 1GKM3XS
+ showCaret();
+ }
+ // go to beginning of R2L segment (visually end of next L2R
+ // segment)/beginning of line
+ caretOffset--;
+ while (caretOffset - lineOffset > 0 &&
+ bidi.isRightToLeft(caretOffset - lineOffset)) {
+ caretOffset--;
+ }
+ }
+ }
+ else
+ if (offsetInLine == lineLength &&
+ bidi.getTextPosition(lineLength) != XINSET) {
+ // at logical line end in R2L segment but there's more text (a
+ // L2R segment) go to end of R2L segment (visually left of next
+ // L2R segment)/end of line
+ caretOffset--;
+ while (caretOffset - lineOffset > 0 &&
+ bidi.isRightToLeft(caretOffset - lineOffset)) {
+ caretOffset--;
+ }
+ }
+ else
+ if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
+ // decrease caret logically if in L2R segment (move visually left)
+ caretOffset--;
+ doSelection(SWT.LEFT);
+ // end of L2R segment reached (visual left side of preceeding R2L
+ // segment)?
+ if (caretOffset - lineOffset > 0 &&
+ bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
+ // go to beginning of R2L segment (visually start of next L2R
+ // segment)/beginning of line
+ caretOffset--;
+ while (caretOffset - lineOffset > 0 &&
+ bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
+ caretOffset--;
+ }
+ }
+ }
+ // if new caret position is to the left of the client area
+ if (bidi.getTextPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
+ // scroll to the caret position
+ showCaret();
+ }
+ else {
+ // otherwise just update caret position without scrolling it into view
+ setBidiCaretLocation(null);
+ setBidiKeyboardLanguage();
+ }
+ // Beginning of line reached (auto scroll finished) but not scrolled
+ // completely to the left? Fixes 1GKM193
+ if (caretOffset - lineOffset == 0 && horizontalScrollOffset > 0 &&
+ horizontalScrollOffset <= XINSET) {
+ scrollHorizontalBar(-horizontalScrollOffset);
+ }
+ }
+ gc.dispose();
+ }
+ else
+ if (offsetInLine > 0) {
+ caretOffset--;
+ showCaret();
+ }
+}
+/**
+ * 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
+ * right (visually right because it's now in a L2R segment).
+ */
+void doColumnRight() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+ String lineText = content.getLine(line);
+ int lineLength = lineText.length();
+
+ if (isBidi()) {
+ GC gc = getGC();
+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
+ if (bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
+ offsetInLine < lineLength) {
+ if (bidi.isRightToLeft(offsetInLine) == false &&
+ offsetInLine < lineLength) {
+ // advance caret logically if in L2R segment (move visually right)
+ caretOffset++;
+ doSelection(SWT.RIGHT);
+ // end of L2R segment reached (visual right side)?
+ if (bidi.isRightToLeft(caretOffset - lineOffset)) {
+ // go to end of R2L segment (visually left of next R2L segment)/
+ // end of line
+ caretOffset++;
+ while (caretOffset < lineOffset + lineLength &&
+ bidi.isRightToLeft(caretOffset - lineOffset)) {
+ caretOffset++;
+ }
+ }
+ }
+ else
+ if (offsetInLine > 0 &&
+ (bidi.isRightToLeft(offsetInLine) ||
+ bidi.getTextWidth() + leftMargin > horizontalScrollOffset + getClientArea().width ||
+ offsetInLine < lineLength)) {
+ // advance caret visually if in R2L segment or logically at line end
+ // but right end of line is not fully visible yet
+ caretOffset--;
+ doSelection(SWT.LEFT);
+ offsetInLine = caretOffset - lineOffset;
+ // end of R2L segment reached (visual right side)?
+ if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine) == false) {
+ // go to end of R2L segment (visually left of next L2R segment)/
+ // end of line
+ caretOffset++;
+ while (caretOffset < lineOffset + lineLength &&
+ bidi.isRightToLeft(caretOffset - lineOffset)) {
+ caretOffset++;
+ }
+ }
+ }
+ else
+ if (offsetInLine == 0 && bidi.getTextPosition(0) != bidi.getTextWidth()) {
+ // at logical line start in R2L segment but there's more text (a L2R
+ // segment) go to end of R2L segment (visually left of next L2R
+ // segment)/end of line
+ caretOffset++;
+ while (caretOffset < lineOffset + lineLength &&
+ bidi.isRightToLeft(caretOffset - lineOffset - 1)) {
+ caretOffset++;
+ }
+ }
+ offsetInLine = caretOffset - lineOffset;
+ // if new caret position is to the right of the client area
+ if (bidi.getTextPosition(offsetInLine) >= horizontalScrollOffset) {
+ // scroll to the caret position
+ showCaret();
+ }
+ else {
+ // otherwise just update caret position without scrolling it into view
+ setBidiCaretLocation(null);
+ setBidiKeyboardLanguage();
+ }
+ if (offsetInLine > 0 && offsetInLine < lineLength - 1) {
+ int clientAreaEnd = horizontalScrollOffset + getClientArea().width;
+ boolean directionChange = bidi.isRightToLeft(offsetInLine - 1) == false && bidi.isRightToLeft(offsetInLine);
+ int textWidth = bidi.getTextWidth() + leftMargin;
+ // between L2R and R2L segment and second character of R2L segment is
+ // left of right border and logical line end is left of right border
+ // but visual line end is not left of right border
+ if (directionChange &&
+ bidi.isRightToLeft(offsetInLine + 1) &&
+ bidi.getTextPosition(offsetInLine + 1) + leftMargin < clientAreaEnd &&
+ bidi.getTextPosition(lineLength) + leftMargin < clientAreaEnd && textWidth > clientAreaEnd) {
+ // make visual line end visible
+ scrollHorizontalBar(textWidth - clientAreaEnd);
+ }
+ }
+ }
+ gc.dispose();
+ }
+ else
+ if (offsetInLine < lineLength) {
+ caretOffset++;
+ showCaret();
+ }
+}
+/**
+ * Replaces the selection with the character or insert the character at the
+ * current caret position if no selection exists.
+ * If a carriage return was typed replace it with the line break character
+ * used by the widget on this platform.
+ * <p>
+ *
+ * @param key the character typed by the user
+ */
+void doContent(char key) {
+ Event event;
+
+ if (textLimit > 0 &&
+ content.getCharCount() - (selection.y - selection.x) >= textLimit) {
+ return;
+ }
+ event = new Event();
+ event.start = selection.x;
+ event.end = selection.y;
+ // replace a CR line break with the widget line break
+ // CR does not make sense on Windows since most (all?) applications
+ // don't recognize CR as a line break.
+ if (key == SWT.CR || key == SWT.LF) {
+ if (isSingleLine() == false) {
+ event.text = getLineDelimiter();
+ }
+ }
+ // no selection and overwrite mode is on and the typed key is not a
+ // tab character (tabs are always inserted without overwriting)?
+ else
+ if (selection.x == selection.y && overwrite == true && key != TAB) {
+ int lineIndex = content.getLineAtOffset(event.end);
+ int lineOffset = content.getOffsetAtLine(lineIndex);
+ String line = content.getLine(lineIndex);
+ // replace character at caret offset if the caret is not at the
+ // end of the line
+ if (event.end < lineOffset + line.length()) {
+ event.end++;
+ }
+ event.text = new String(new char[] {key});
+ }
+ else {
+ event.text = new String(new char[] {key});
+ }
+ if (event.text != null) {
+ sendKeyEvent(event);
+ }
+}
+/**
+ * Moves the caret after the last character of the widget content.
+ */
+void doContentEnd() {
+ // place caret at end of first line if receiver is in single
+ // line mode. fixes 4820.
+ if (isSingleLine()) {
+ doLineEnd();
+ }
+ else {
+ int length = content.getCharCount();
+ if (caretOffset < length) {
+ caretOffset = length;
+ showCaret();
+ }
+ }
+}
+/**
+ * Moves the caret in front of the first character of the widget content.
+ */
+void doContentStart() {
+ if (caretOffset > 0) {
+ caretOffset = 0;
+ showCaret();
+ }
+}
+/**
+ * Moves the caret to the start of the selection if a selection exists.
+ * Otherwise, if no selection exists move the cursor according to the
+ * cursor selection rules.
+ * <p>
+ *
+ * @see #doSelectionCursorPrevious
+ */
+void doCursorPrevious() {
+ if (selection.y - selection.x > 0) {
+ int caretLine;
+
+ caretOffset = selection.x;
+ caretLine = getCaretLine();
+ showCaret(caretLine);
+ }
+ else {
+ doSelectionCursorPrevious();
+ }
+}
+/**
+ * Moves the caret to the end of the selection if a selection exists.
+ * Otherwise, if no selection exists move the cursor according to the
+ * cursor selection rules.
+ * <p>
+ *
+ * @see #doSelectionCursorNext
+ */
+void doCursorNext() {
+ if (selection.y - selection.x > 0) {
+ int caretLine;
+
+ caretOffset = selection.y;
+ caretLine = getCaretLine();
+ showCaret(caretLine);
+ }
+ else {
+ doSelectionCursorNext();
+ }
+}
+/**
+ * Deletes the next character. Delete the selected text if any.
+ */
+void doDelete() {
+ Event event = new Event();
+
+ event.text = "";
+ if (selection.x != selection.y) {
+ event.start = selection.x;
+ event.end = selection.y;
+ sendKeyEvent(event);
+ }
+ else
+ if (caretOffset < content.getCharCount()) {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int lineLength = content.getLine(line).length();
+
+ if (caretOffset == lineOffset + lineLength) {
+ event.start = caretOffset;
+ event.end = content.getOffsetAtLine(line + 1);
+ }
+ else {
+ event.start = caretOffset;
+ event.end = caretOffset + 1;
+ }
+ sendKeyEvent(event);
+ }
+}
+/**
+ * Deletes the next word.
+ */
+void doDeleteWordNext() {
+ if (selection.x != selection.y) {
+ // if a selection exists, treat the as if
+ // only the delete key was pressed
+ doDelete();
+ } else {
+ Event event = new Event();
+ event.text = "";
+ event.start = caretOffset;
+ event.end = getWordEnd(caretOffset);
+ sendKeyEvent(event);
+ }
+}
+/**
+ * Deletes the previous word.
+ */
+void doDeleteWordPrevious() {
+ if (selection.x != selection.y) {
+ // if a selection exists, treat as if
+ // only the backspace key was pressed
+ doBackspace();
+ } else {
+ Event event = new Event();
+ event.text = "";
+ event.start = getWordStart(caretOffset);
+ event.end = caretOffset;
+ sendKeyEvent(event);
+ }
+}
+/**
+ * Moves the caret one line down and to the same character offset relative
+ * to the beginning of the line. Move the caret to the end of the new line
+ * if the new line is shorter than the character offset.
+ *
+ * @return index of the new line relative to the first line in the document
+ */
+int doLineDown() {
+ if (isSingleLine()) {
+ return 0;
+ }
+ // allow line down action only if receiver is not in single line mode.
+ // fixes 4820.
+ int caretLine = getCaretLine();
+ if (caretLine < content.getLineCount() - 1) {
+ caretLine++;
+ if (isBidi()) {
+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
+ }
+ else {
+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
+ }
+ }
+ return caretLine;
+}
+/**
+ * Moves the caret to the end of the line.
+ */
+void doLineEnd() {
+ int caretLine = getCaretLine();
+ int lineOffset = content.getOffsetAtLine(caretLine);
+ int lineLength = content.getLine(caretLine).length();
+ int lineEndOffset = lineOffset + lineLength;
+
+ if (caretOffset < lineEndOffset) {
+ caretOffset = lineEndOffset;
+ showCaret();
+ }
+}
+/**
+ * Moves the caret to the beginning of the line.
+ */
+void doLineStart() {
+ int caretLine = getCaretLine();
+ int lineOffset = content.getOffsetAtLine(caretLine);
+
+ if (caretOffset > lineOffset) {
+ caretOffset = lineOffset;
+ showCaret(caretLine);
+ }
+}
+/**
+ * Moves the caret one line up and to the same character offset relative
+ * to the beginning of the line. Move the caret to the end of the new line
+ * if the new line is shorter than the character offset.
+ *
+ * @return index of the new line relative to the first line in the document
+ */
+int doLineUp() {
+ int caretLine = getCaretLine();
+
+ if (caretLine > 0) {
+ caretLine--;
+ if (isBidi()) {
+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
+ }
+ else {
+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
+ }
+ }
+ return caretLine;
+}
+/**
+ * Moves the caret to the specified location.
+ * <p>
+ *
+ * @param x x location of the new caret position
+ * @param y y location of the new caret position
+ * @param select the location change is a selection operation.
+ * include the line delimiter in the selection
+ */
+void doMouseLocationChange(int x, int y, boolean select) {
+ int line = (y + verticalScrollOffset) / lineHeight;
+ int lineCount = content.getLineCount();
+ int newCaretOffset;
+ int newCaretLine;
+
+ if (line > lineCount - 1) {
+ line = lineCount - 1;
+ }
+ // allow caret to be placed below first line only if receiver is
+ // not in single line mode. fixes 4820.
+ if (line < 0 || (isSingleLine() && line > 0)) {
+ return;
+ }
+ if (isBidi()) {
+ newCaretOffset = getBidiOffsetAtMouseLocation(x, line);
+ }
+ else {
+ newCaretOffset = getOffsetAtMouseLocation(x, line);
+ }
+ if (mouseDoubleClick) {
+ // double click word select the previous/next word. fixes bug 15610
+ newCaretOffset = doMouseWordSelect(x, newCaretOffset, line);
+ }
+ newCaretLine = content.getLineAtOffset(newCaretOffset);
+ // Is the mouse within the left client area border or on
+ // a different line? If not the autoscroll selection
+ // could be incorrectly reset. Fixes 1GKM3XS
+ if (y >= 0 && y < getClientArea().height &&
+ (x >= 0 || newCaretLine != content.getLineAtOffset(caretOffset))) {
+ if (newCaretOffset != caretOffset) {
+ caretOffset = newCaretOffset;
+ if (select) {
+ doMouseSelection();
+ }
+ showCaret();
+ }
+ }
+ if (select == false) {
+ clearSelection(true);
+ }
+}
+/**
+ * Updates the selection based on the caret position
+ */
+void doMouseSelection() {
+ if (caretOffset <= selection.x ||
+ (caretOffset > selection.x &&
+ caretOffset < selection.y && selectionAnchor == selection.x)) {
+ doSelection(SWT.LEFT);
+ }
+ else {
+ doSelection(SWT.RIGHT);
+ }
+}
+/**
+ * Returns the offset of the word at the specified offset.
+ * If the current selection extends from high index to low index
+ * (i.e., right to left, or caret is at left border of selecton on
+ * non-bidi platforms) the start offset of the word preceeding the
+ * selection is returned. If the current selection extends from
+ * low index to high index the end offset of the word following
+ * the selection is returned.
+ *
+ * @param x mouse x location
+ * @param newCaretOffset caret offset of the mouse cursor location
+ * @param line line index of the mouse cursor location
+ */
+int doMouseWordSelect(int x, int newCaretOffset, int line) {
+ int wordOffset;
+
+ // flip selection anchor based on word selection direction from
+ // base double click. Always do this here (and don't rely on doAutoScroll)
+ // because auto scroll only does not cover all possible mouse selections
+ // (e.g., mouse x < 0 && mouse y > caret line y)
+ if (newCaretOffset < selectionAnchor && selectionAnchor == selection.x) {
+ selectionAnchor = doubleClickSelection.y;
+ }
+ else
+ if (newCaretOffset > selectionAnchor && selectionAnchor == selection.y) {
+ selectionAnchor = doubleClickSelection.x;
+ }
+ if (x >= 0 && x < getClientArea().width) {
+ // find the previous/next word
+ if (caretOffset == selection.x) {
+ wordOffset = getWordStart(newCaretOffset);
+ }
+ else {
+ wordOffset = getWordEndNoSpaces(newCaretOffset);
+ }
+ // mouse word select only on same line mouse cursor is on
+ if (content.getLineAtOffset(wordOffset) == line) {
+ newCaretOffset = wordOffset;
+ }
+ }
+ return newCaretOffset;
+}
+/**
+ * Scrolls one page down so that the last line (truncated or whole)
+ * of the current page becomes the fully visible top line.
+ * The caret is scrolled the same number of lines so that its location
+ * relative to the top line remains the same. The exception is the end
+ * of the text where a full page scroll is not possible. In this case
+ * the caret is moved after the last character.
+ * <p>
+ *
+ * @param select whether or not to select the page
+ */
+void doPageDown(boolean select) {
+ int lineCount = content.getLineCount();
+ int oldColumnX = columnX;
+ int caretLine;
+
+ // do nothing if in single line mode. fixes 5673
+ if (isSingleLine()) {
+ return;
+ }
+ caretLine = getCaretLine();
+ if (caretLine < lineCount - 1) {
+ int verticalMaximum = lineCount * getVerticalIncrement();
+ int pageSize = getClientArea().height;
+ int scrollLines = Math.min(lineCount - caretLine - 1, getLineCountWhole());
+ int scrollOffset;
+
+ // ensure that scrollLines never gets negative and at leat one
+ // line is scrolled. fixes bug 5602.
+ scrollLines = Math.max(1, scrollLines);
+ caretLine += scrollLines;
+ if (isBidi()) {
+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
+ }
+ else {
+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
+ }
+ if (select) {
+ doSelection(SWT.RIGHT);
+ }
+ // scroll one page down or to the bottom
+ scrollOffset = verticalScrollOffset + scrollLines * getVerticalIncrement();
+ if (scrollOffset + pageSize > verticalMaximum) {
+ scrollOffset = verticalMaximum - pageSize;
+ }
+ if (scrollOffset > verticalScrollOffset) {
+ setVerticalScrollOffset(scrollOffset, true);
+ }
+ }
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ // save the original horizontal caret position
+ columnX = oldColumnX;
+}
+/**
+ * Moves the cursor to the end of the last fully visible line.
+ */
+void doPageEnd() {
+ // go to end of line if in single line mode. fixes 5673
+ if (isSingleLine()) {
+ doLineEnd();
+ }
+ else {
+ int line = getBottomIndex();
+ int bottomCaretOffset = content.getOffsetAtLine(line) + content.getLine(line).length();
+
+ if (caretOffset < bottomCaretOffset) {
+ caretOffset = bottomCaretOffset;
+ showCaret();
+ }
+ }
+}
+/**
+ * Moves the cursor to the beginning of the first fully visible line.
+ */
+void doPageStart() {
+ int topCaretOffset = content.getOffsetAtLine(topIndex);
+
+ if (caretOffset > topCaretOffset) {
+ caretOffset = topCaretOffset;
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(topIndex);
+ }
+}
+/**
+ * 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
+ * of the text where a full page scroll is not possible. In this case the
+ * caret is moved in front of the first character.
+ */
+void doPageUp() {
+ int oldColumnX = columnX;
+ int caretLine = getCaretLine();
+
+ if (caretLine > 0) {
+ int scrollLines = Math.max(1, Math.min(caretLine, getLineCountWhole()));
+ int scrollOffset;
+
+ caretLine -= scrollLines;
+ if (isBidi()) {
+ caretOffset = getBidiOffsetAtMouseLocation(columnX, caretLine);
+ }
+ else {
+ caretOffset = getOffsetAtMouseLocation(columnX, caretLine);
+ }
+ // scroll one page up or to the top
+ scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
+ if (scrollOffset < verticalScrollOffset) {
+ setVerticalScrollOffset(scrollOffset, true);
+ }
+ }
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ // save the original horizontal caret position
+ columnX = oldColumnX;
+}
+/**
+ * Updates the selection to extend to the current caret position.
+ */
+void doSelection(int direction) {
+ int redrawStart = -1;
+ int redrawEnd = -1;
+
+ if (selectionAnchor == -1) {
+ selectionAnchor = selection.x;
+ }
+ if (direction == SWT.LEFT) {
+ if (caretOffset < selection.x) {
+ // grow selection
+ redrawEnd = selection.x;
+ redrawStart = selection.x = caretOffset;
+ // check if selection has reversed direction
+ if (selection.y != selectionAnchor) {
+ redrawEnd = selection.y;
+ selection.y = selectionAnchor;
+ }
+ }
+ else // test whether selection actually changed. Fixes 1G71EO1
+ if (selectionAnchor == selection.x && caretOffset < selection.y) {
+ // caret moved towards selection anchor (left side of selection).
+ // shrink selection
+ redrawEnd = selection.y;
+ redrawStart = selection.y = caretOffset;
+ }
+ }
+ else {
+ if (caretOffset > selection.y) {
+ // grow selection
+ redrawStart = selection.y;
+ redrawEnd = selection.y = caretOffset;
+ // check if selection has reversed direction
+ if (selection.x != selectionAnchor) {
+ redrawStart = selection.x;
+ selection.x = selectionAnchor;
+ }
+ }
+ else // test whether selection actually changed. Fixes 1G71EO1
+ if (selectionAnchor == selection.y && caretOffset > selection.x) {
+ // caret moved towards selection anchor (right side of selection).
+ // shrink selection
+ redrawStart = selection.x;
+ redrawEnd = selection.x = caretOffset;
+ }
+ }
+ if (redrawStart != -1 && redrawEnd != -1) {
+ internalRedrawRange(redrawStart, redrawEnd - redrawStart, true);
+ sendSelectionEvent();
+ }
+}
+/**
+ * Moves the caret to the next character or to the beginning of the
+ * next line if the cursor is at the end of a line.
+ */
+void doSelectionCursorNext() {
+ int caretLine = getCaretLine();
+ int lineOffset = content.getOffsetAtLine(caretLine);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (offsetInLine < content.getLine(caretLine).length()) {
+ // Remember the last direction. Always update lastCaretDirection,
+ // even though it's not used in non-bidi mode in order to avoid
+ // extra methods.
+ lastCaretDirection = ST.COLUMN_NEXT;
+ caretOffset++;
+ showCaret();
+ }
+ else
+ if (caretLine < content.getLineCount() - 1 && isSingleLine() == false) {
+ // only go to next line if not in single line mode. fixes 5673
+ caretLine++;
+ caretOffset = content.getOffsetAtLine(caretLine);
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ }
+}
+/**
+ * Moves the caret to the previous character or to the end of the previous
+ * line if the cursor is at the beginning of a line.
+ */
+void doSelectionCursorPrevious() {
+ int caretLine = getCaretLine();
+ int lineOffset = content.getOffsetAtLine(caretLine);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (offsetInLine > 0) {
+ // Remember the last direction. Always update lastCaretDirection,
+ // even though it's not used in non-bidi mode in order to avoid
+ // extra methods.
+ lastCaretDirection = ST.COLUMN_PREVIOUS;
+ caretOffset--;
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ }
+ else
+ if (caretLine > 0) {
+ caretLine--;
+ lineOffset = content.getOffsetAtLine(caretLine);
+ caretOffset = lineOffset + content.getLine(caretLine).length();
+ showCaret();
+ }
+}
+/**
+ * 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
+ * last line.
+ * Adjusts the selection according to the caret change. This can either add
+ * to or subtract from the old selection, depending on the previous selection
+ * direction.
+ */
+void doSelectionLineDown() {
+ int oldColumnX;
+ int caretLine;
+ int lineStartOffset;
+
+ if (isSingleLine()) {
+ return;
+ }
+ caretLine = getCaretLine();
+ lineStartOffset = content.getOffsetAtLine(caretLine);
+ // reset columnX on selection
+ oldColumnX = columnX = getXAtOffset(
+ content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
+ if (caretLine == content.getLineCount() - 1) {
+ caretOffset = content.getCharCount();
+ }
+ else {
+ caretLine = doLineDown();
+ }
+ setMouseWordSelectionAnchor();
+ // select first and then scroll to reduce flash when key
+ // repeat scrolls lots of lines
+ doSelection(SWT.RIGHT);
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ // save the original horizontal caret position
+ columnX = oldColumnX;
+}
+/**
+ * Moves the caret one line up and to the same character offset relative
+ * to the beginning of the line. Moves the caret to the end of the new line
+ * 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.
+ * Adjusts the selection according to the caret change. This can either add
+ * to or subtract from the old selection, depending on the previous selection
+ * direction.
+ */
+void doSelectionLineUp() {
+ int oldColumnX;
+ int caretLine = getCaretLine();
+ int lineStartOffset = content.getOffsetAtLine(caretLine);
+
+ // reset columnX on selection
+ oldColumnX = columnX = getXAtOffset(
+ content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
+ if (caretLine == 0) {
+ caretOffset = 0;
+ }
+ else {
+ caretLine = doLineUp();
+ }
+ setMouseWordSelectionAnchor();
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ doSelection(SWT.LEFT);
+ // save the original horizontal caret position
+ columnX = oldColumnX;
+}
+/**
+ * Scrolls one page down so that the last line (truncated or whole)
+ * of the current page becomes the fully visible top line.
+ * The caret is scrolled the same number of lines so that its location
+ * relative to the top line remains the same. The exception is the end
+ * of the text where a full page scroll is not possible. In this case
+ * the caret is moved after the last character.
+ * <p>
+ * Adjusts the selection according to the caret change. This can either add
+ * to or subtract from the old selection, depending on the previous selection
+ * direction.
+ * </p>
+ */
+void doSelectionPageDown() {
+ int oldColumnX;
+ int caretLine = getCaretLine();
+ int lineStartOffset = content.getOffsetAtLine(caretLine);
+
+ // reset columnX on selection
+ oldColumnX = columnX = getXAtOffset(
+ content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
+ doPageDown(true);
+ columnX = oldColumnX;
+}
+/**
+ * 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
+ * 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>
+ * Adjusts the selection according to the caret change. This can either add
+ * to or subtract from the old selection, depending on the previous selection
+ * direction.
+ * </p>
+ */
+void doSelectionPageUp() {
+ int oldColumnX;
+ int caretLine = getCaretLine();
+ int lineStartOffset = content.getOffsetAtLine(caretLine);
+
+ // reset columnX on selection
+ oldColumnX = columnX = getXAtOffset(
+ content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
+ doPageUp();
+ columnX = oldColumnX;
+}
+/**
+ * Moves the caret to the end of the next word .
+ */
+void doSelectionWordNext() {
+ int newCaretOffset = getWordEnd(caretOffset);
+
+ // don't change caret position if in single line mode and the cursor
+ // would be on a different line. fixes 5673
+ if (isSingleLine() == false ||
+ content.getLineAtOffset(caretOffset) == content.getLineAtOffset(newCaretOffset)) {
+ lastCaretDirection = ST.COLUMN_NEXT;
+ caretOffset = newCaretOffset;
+ showCaret();
+ }
+}
+/**
+ * Moves the caret to the start of the previous word.
+ */
+void doSelectionWordPrevious() {
+ int caretLine;
+
+ lastCaretDirection = ST.COLUMN_PREVIOUS;
+ caretOffset = getWordStart(caretOffset);
+ caretLine = content.getLineAtOffset(caretOffset);
+ // word previous always comes from bottom line. when
+ // wrapping lines, stay on bottom line when on line boundary
+ if (wordWrap && caretLine < content.getLineCount() - 1 &&
+ caretOffset == content.getOffsetAtLine(caretLine + 1)) {
+ caretLine++;
+ }
+ showCaret(caretLine);
+}
+/**
+ * Moves the caret to the end of the next word.
+ * If a selection exists, move the caret to the end of the selection
+ * and remove the selection.
+ */
+void doWordNext() {
+ if (selection.y - selection.x > 0) {
+ int caretLine;
+
+ caretOffset = selection.y;
+ caretLine = getCaretLine();
+ showCaret(caretLine);
+ }
+ else {
+ doSelectionWordNext();
+ }
+}
+/**
+ * Moves the caret to the start of the previous word.
+ * If a selection exists, move the caret to the start of the selection
+ * and remove the selection.
+ */
+void doWordPrevious() {
+ if (selection.y - selection.x > 0) {
+ int caretLine;
+
+ caretOffset = selection.x;
+ caretLine = getCaretLine();
+ showCaret(caretLine);
+ }
+ else {
+ doSelectionWordPrevious();
+ }
+}
+/**
+ * Draws the specified rectangle.
+ * Draw directly without invalidating the affected area when clearBackground is
+ * false.
+ * <p>
+ *
+ * @param x the x position
+ * @param y the y position
+ * @param width the width
+ * @param height the height
+ * @param clearBackground true=clear the background by invalidating the requested
+ * redraw area, false=draw the foreground directly without invalidating the
+ * redraw area.
+ */
+void draw(int x, int y, int width, int height, boolean clearBackground) {
+ if (clearBackground) {
+ redraw(x + leftMargin, y + topMargin, width, height, true);
+ }
+ else {
+ int startLine = (y + verticalScrollOffset) / lineHeight;
+ int endY = y + height;
+ int paintYFromTopLine = (startLine - topIndex) * lineHeight;
+ int topLineOffset = (topIndex * lineHeight - verticalScrollOffset);
+ int paintY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling
+ int lineCount = content.getLineCount();
+ Color background = getBackground();
+ Color foreground = getForeground();
+ GC gc = getGC();
+
+ if (isSingleLine()) {
+ lineCount = 1;
+ if (startLine > 1) {
+ startLine = 1;
+ }
+ }
+ for (int i = startLine; paintY < endY && i < lineCount; i++, paintY += lineHeight) {
+ String line = content.getLine(i);
+ renderer.drawLine(line, i, paintY, gc, background, foreground, clearBackground);
+ }
+ gc.dispose();
+ }
+}
+/**
+ * Ends the autoscroll process.
+ */
+void endAutoScroll() {
+ autoScrollDirection = SWT.NULL;
+}
+/**
+ * @see org.eclipse.swt.widgets.Control#getBackground
+ */
+public Color getBackground() {
+ checkWidget();
+ if (background == null) {
+ return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+ }
+ return background;
+}
+/**
+ * Gets the BIDI coloring mode. When true the BIDI text display
+ * algorithm is applied to segments of text that are the same
+ * color.
+ *
+ * @return the current coloring mode
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * <p>
+ * @deprecated use BidiSegmentListener instead.
+ * </p>
+ */
+public boolean getBidiColoring() {
+ checkWidget();
+ return bidiColoring;
+}
+/**
+ * Returns the offset at the specified x location in the specified line.
+ * Also sets the caret direction so that the caret is placed correctly
+ * depending on whether the mouse location is in a R2L or L2R segment.
+ * <p>
+ *
+ * @param x x location of the mouse location
+ * @param line line the mouse location is in
+ * @return the offset at the specified x location in the specified line,
+ * relative to the beginning of the document
+ */
+int getBidiOffsetAtMouseLocation(int x, int line) {
+ String lineText = content.getLine(line);
+ int lineOffset = content.getOffsetAtLine(line);
+ GC gc = getGC();
+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
+ int[] values;
+ int offsetInLine;
+ x += horizontalScrollOffset;
+ values = bidi.getCaretOffsetAndDirectionAtX(x - leftMargin);
+ offsetInLine = values[0];
+ lastCaretDirection = values[1];
+ gc.dispose();
+
+ return lineOffset + offsetInLine;
+}
+/**
+ * Returns the x position of the character at the specified offset
+ * relative to the first character in the line.
+ * </p>
+ *
+ * @param text text to be measured.
+ * @param endOffset offset of the character
+ * @param bidi the bidi object to use for measuring text in bidi
+ * locales.
+ * @return x position of the character at the specified offset.
+ * 0 if the length is outside the specified text.
+ */
+int getBidiTextPosition(String text, int endOffset, StyledTextBidi bidi) {
+ if (endOffset > text.length()) {
+ return 0;
+ }
+ // Use lastCaretDirection in order to get same results as during
+ // caret positioning (setBidiCaretLocation). Fixes 1GKU4C5.
+ return bidi.getTextPosition(endOffset, lastCaretDirection);
+}
+/**
+ * Returns the index of the last fully visible line.
+ * <p>
+ *
+ * @return index of the last fully visible line.
+ */
+int getBottomIndex() {
+ int lineCount = 1;
+
+ if (lineHeight != 0) {
+ // calculate the number of lines that are fully visible
+ int partialTopLineHeight = topIndex * lineHeight - verticalScrollOffset;
+ lineCount = (getClientArea().height - partialTopLineHeight) / lineHeight;
+ }
+ return Math.min(content.getLineCount() - 1, topIndex + Math.max(0, lineCount - 1));
+}
+/**
+ * Returns the caret position relative to the start of the text.
+ * <p>
+ *
+ * @return the caret position relative to the start of the text.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getCaretOffset() {
+ checkWidget();
+
+ return caretOffset;
+}
+/**
+ * Returns the caret offset at the given x location in the line.
+ * The caret offset is the offset of the character where the caret will be
+ * placed when a mouse click occurs. The caret offset will be the offset of
+ * the character after the clicked one if the mouse click occurs at the second
+ * half of a character.
+ * Doesn't properly handle ligatures and other context dependent characters
+ * unless the current locale is a bidi locale.
+ * Ligatures are handled properly as long as they don't occur at lineXOffset.
+ * <p>
+ *
+ * @param line text of the line to calculate the offset in
+ * @param lineOffset offset of the first character in the line.
+ * 0 based from the beginning of the document.
+ * @param lineXOffset x location in the line
+ * @return caret offset at the x location relative to the start of the line.
+ */
+int getCaretOffsetAtX(String line, int lineOffset, int lineXOffset) {
+ int offset = 0;
+ GC gc = getGC();
+ StyleRange[] styles = null;
+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
+
+ lineXOffset += horizontalScrollOffset;
+ if (event != null) {
+ styles = renderer.filterLineStyles(event.styles);
+ }
+ int low = -1;
+ int high = line.length();
+ while (high - low > 1) {
+ offset = (high + low) / 2;
+ int x = renderer.getTextPosition(line, lineOffset, offset, styles, gc) + leftMargin;
+ int charWidth = renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc) + leftMargin - x;
+ if (lineXOffset <= x + charWidth / 2) {
+ high = offset;
+ }
+ else {
+ low = offset;
+ }
+ }
+ offset = high;
+ gc.dispose();
+ return offset;
+}
+/**
+ * Returns the caret width.
+ * <p>
+ *
+ * @return the caret width, 0 if caret is null.
+ */
+int getCaretWidth() {
+ Caret caret = getCaret();
+ if (caret == null) return 0;
+ return caret.getSize().x;
+}
+/**
+ * Returns the content implementation that is used for text storage
+ * or null if no user defined content implementation has been set.
+ * <p>
+ *
+ * @return content implementation that is used for text storage or null
+ * if no user defined content implementation has been set.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public StyledTextContent getContent() {
+ checkWidget();
+
+ return logicalContent;
+}
+/**
+ * Returns whether the widget implements double click mouse behavior.
+ * <p>
+ *
+ * @return true if double clicking a word selects the word, false if double clicks
+ * have the same effect as regular mouse clicks
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getDoubleClickEnabled() {
+ checkWidget();
+ return doubleClickEnabled;
+}
+/**
+ * Returns whether the widget content can be edited.
+ * <p>
+ *
+ * @return true if content can be edited, false otherwise
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getEditable() {
+ checkWidget();
+ return editable;
+}
+/**
+ * @see org.eclipse.swt.widgets.Control#getForeground
+ */
+public Color getForeground() {
+ checkWidget();
+ if (foreground == null) {
+ return getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
+ }
+ return foreground;
+}
+/**
+ * Return a GC to use for rendering and update the cached font style to
+ * represent the current style.
+ * <p>
+ *
+ * @return GC.
+ */
+GC getGC() {
+ renderer.setCurrentFontStyle(SWT.NORMAL);
+ return new GC(this);
+}
+/**
+ * Returns the horizontal scroll increment.
+ * <p>
+ *
+ * @return horizontal scroll increment.
+ */
+int getHorizontalIncrement() {
+ GC gc = getGC();
+ int increment = gc.getFontMetrics().getAverageCharWidth();
+
+ gc.dispose();
+ return increment;
+}
+/**
+ * Returns the horizontal scroll offset relative to the start of the line.
+ * <p>
+ *
+ * @return horizontal scroll offset relative to the start of the line,
+ * measured in character increments starting at 0, if > 0 the content is scrolled
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getHorizontalIndex() {
+ checkWidget();
+ return horizontalScrollOffset / getHorizontalIncrement();
+}
+/**
+ * Returns the horizontal scroll offset relative to the start of the line.
+ * <p>
+ *
+ * @return the horizontal scroll offset relative to the start of the line,
+ * measured in pixel starting at 0, if > 0 the content is scrolled.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getHorizontalPixel() {
+ checkWidget();
+ return horizontalScrollOffset;
+}
+/**
+ * Returns the action assigned to the key.
+ * Returns SWT.NULL if there is no action associated with the key.
+ * <p>
+ *
+ * @param key a key code defined in SWT.java or a character.
+ * Optionally ORd with a state mask. Preferred state masks are one or more of
+ * SWT.MOD1, SWT.MOD2, SWT.MOD3, since these masks account for modifier platform
+ * differences. However, there may be cases where using the specific state masks
+ * (i.e., SWT.CTRL, SWT.SHIFT, SWT.ALT, SWT.COMMAND) makes sense.
+ * @return one of the predefined actions defined in ST.java or SWT.NULL
+ * if there is no action associated with the key.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getKeyBinding(int key) {
+ checkWidget();
+ Integer action = (Integer) keyActionMap.get(new Integer(key));
+ int intAction;
+
+ if (action == null) {
+ intAction = SWT.NULL;
+ }
+ else {
+ intAction = action.intValue();
+ }
+ return intAction;
+}
+/**
+ * Gets the number of characters.
+ * <p>
+ *
+ * @return number of characters in the widget
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getCharCount() {
+ checkWidget();
+ return content.getCharCount();
+}
+/**
+ * Returns the background color of the line at the given index.
+ * Returns null if a LineBackgroundListener has been set or if no background
+ * 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.
+ * <p>
+ *
+ * @return the background color of the line at the given index.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
+ * </ul>
+ */
+public Color getLineBackground(int index) {
+ checkWidget();
+ Color lineBackground = null;
+
+ if (index < 0 || index > logicalContent.getLineCount()) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (userLineBackground == false) {
+ lineBackground = defaultLineStyler.getLineBackground(index);
+ }
+ return lineBackground;
+}
+/**
+ * Returns the line background data for the given line or null if
+ * there is none.
+ * <p>
+ * @param lineOffset offset of the line start relative to the start
+ * of the content.
+ * @param line line to get line background data for
+ * @return line background data for the given line.
+ */
+StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
+ return sendLineEvent(LineGetBackground, lineOffset, line);
+}
+/**
+ * Gets the number of text lines.
+ * <p>
+ *
+ * @return the number of lines in the widget
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getLineCount() {
+ checkWidget();
+ return getLineAtOffset(getCharCount()) + 1;
+}
+/**
+ * Returns the number of lines that can be completely displayed in the
+ * widget client area.
+ * <p>
+ *
+ * @return number of lines that can be completely displayed in the widget
+ * client area.
+ */
+int getLineCountWhole() {
+ int lineCount;
+
+ if (lineHeight != 0) {
+ lineCount = getClientArea().height / lineHeight;
+ }
+ else {
+ lineCount = 1;
+ }
+ return lineCount;
+}
+/**
+ * Returns the line at the specified offset in the text.
+ * 0 <= offset <= getCharCount() so that getLineAtOffset(getCharCount())
+ * returns the line of the insert location.
+ * <p>
+ *
+ * @param offset offset relative to the start of the content.
+ * 0 <= offset <= getCharCount()
+ * @return line at the specified offset in the text
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
+ * </ul>
+ */
+public int getLineAtOffset(int offset) {
+ checkWidget();
+
+ if (offset < 0 || offset > getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ return logicalContent.getLineAtOffset(offset);
+}
+/**
+ * Returns the line delimiter used for entering new lines by key down
+ * or paste operation.
+ * <p>
+ *
+ * @return line delimiter used for entering new lines by key down
+ * or paste operation.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getLineDelimiter() {
+ checkWidget();
+ return content.getLineDelimiter();
+}
+/**
+ * Returns a StyledTextEvent that can be used to request data such
+ * as styles and background color for a line.
+ * The specified line may be a visual (wrapped) line if in word
+ * wrap mode. The returned object will always be for a logical
+ * (unwrapped) line.
+ * <p>
+ *
+ * @param lineOffset offset of the line. This may be the offset of
+ * a visual line if the widget is in word wrap mode.
+ * @param line line text. This may be the text of a visualline if
+ * the widget is in word wrap mode.
+ * @return StyledTextEvent that can be used to request line data
+ * for the given line.
+ */
+StyledTextEvent sendLineEvent(int eventType, int lineOffset, String line) {
+ StyledTextEvent event = null;
+
+ if (isListening(eventType)) {
+ event = new StyledTextEvent(logicalContent);
+ if (wordWrap) {
+ // if word wrap is on, the line offset and text may be visual (wrapped)
+ int lineIndex = logicalContent.getLineAtOffset(lineOffset);
+
+ event.detail = logicalContent.getOffsetAtLine(lineIndex);
+ event.text = logicalContent.getLine(lineIndex);
+ }
+ else {
+ event.detail = lineOffset;
+ event.text = line;
+ }
+ notifyListeners(eventType, event);
+ }
+ return event;
+}
+/**
+ * Returns the line height.
+ * <p>
+ *
+ * @return line height in pixel.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getLineHeight() {
+ checkWidget();
+ return lineHeight;
+}
+/**
+ * Returns a LineCache implementation. Depending on whether or not
+ * word wrap is on this may be a line wrapping or line width
+ * calculating implementaiton.
+ * <p>
+ *
+ * @param content StyledTextContent to create the LineCache on.
+ * @return a LineCache implementation
+ */
+LineCache getLineCache(StyledTextContent content) {
+ LineCache lineCache;
+
+ if (wordWrap) {
+ lineCache = new WordWrapCache(this, (WrappedContent) content);
+ }
+ else {
+ lineCache = new ContentWidthCache(this, content.getLineCount());
+ }
+ return lineCache;
+}
+/**
+ * Returns the line style data for the given line or null if there is
+ * none. If there is a LineStyleListener but it does not set any styles,
+ * the StyledTextEvent.styles field will be initialized to an empty
+ * array.
+ * <p>
+ *
+ * @param lineOffset offset of the line start relative to the start of
+ * the content.
+ * @param line line to get line styles for
+ * @return line style data for the given line. Styles may start before
+ * line start and end after line end
+ */
+StyledTextEvent getLineStyleData(int lineOffset, String line) {
+ return sendLineEvent(LineGetStyle, lineOffset, line);
+}
+/**
+ * Returns the x, y location of the upper left corner of the character
+ * bounding box at the specified offset in the text. The point is
+ * relative to the upper left corner of the widget client area.
+ * <p>
+ *
+ * @param offset offset relative to the start of the content.
+ * 0 <= offset <= getCharCount()
+ * @return x, y location of the upper left corner of the character
+ * bounding box at the specified offset in the text.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
+ * </ul>
+ */
+public Point getLocationAtOffset(int offset) {
+ checkWidget();
+ if (offset < 0 || offset > getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ int line = content.getLineAtOffset(offset);
+ int lineOffset = content.getOffsetAtLine(line);
+ String lineContent = content.getLine(line);
+ int x = getXAtOffset(lineContent, line, offset - lineOffset);
+ int y = line * lineHeight - verticalScrollOffset;
+
+ return new Point(x, y);
+}
+/**
+ * Returns the character offset of the first character of the given line.
+ * <p>
+ *
+ * @param lineIndex index of the line, 0 based relative to the first
+ * line in the content. 0 <= lineIndex < getLineCount(), except
+ * lineIndex may always be 0
+ * @return offset offset of the first character of the line, relative to
+ * the beginning of the document. The first character of the document is
+ * at offset 0.
+ * When there are not any lines, getOffsetAtLine(0) is a valid call that
+ * answers 0.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when the offset is outside the valid range (< 0 or > getCharCount())</li>
+ * </ul>
+ * @since 2.0
+ */
+public int getOffsetAtLine(int lineIndex) {
+ checkWidget();
+
+ if (lineIndex < 0 ||
+ (lineIndex > 0 && lineIndex >= logicalContent.getLineCount())) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ return logicalContent.getOffsetAtLine(lineIndex);
+}
+/**
+ * Returns the offset of the character at the given location relative
+ * to the first character in the document.
+ * The return value reflects the character offset that the caret will
+ * be placed at if a mouse click occurred at the specified location.
+ * If the x coordinate of the location is beyond the center of a character
+ * the returned offset will be behind the character.
+ * <p>
+ *
+ * @param point the origin of character bounding box relative to
+ * the origin of the widget client area.
+ * @return offset of the character at the given location relative
+ * to the first character in the document.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when point is null</li>
+ * <li>ERROR_INVALID_ARGUMENT when there is no character at the specified location</li>
+ * </ul>
+ */
+public int getOffsetAtLocation(Point point) {
+ checkWidget();
+ int line;
+ int lineOffset;
+ int offsetInLine;
+ String lineText;
+
+ if (point == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ // is y above first line or is x before first column?
+ if (point.y + verticalScrollOffset < 0 || point.x + horizontalScrollOffset < 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ line = (getTopPixel() + point.y) / lineHeight;
+ // does the referenced line exist?
+ if (line >= content.getLineCount()) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ lineText = content.getLine(line);
+ lineOffset = content.getOffsetAtLine(line);
+ offsetInLine = getOffsetAtX(lineText, lineOffset, point.x);
+ // is the x position within the line?
+ if (offsetInLine == -1) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ return lineOffset + offsetInLine;
+}
+/**
+ * Returns the offset at the specified x location in the specified line.
+ * <p>
+ *
+ * @param x x location of the mouse location
+ * @param line line the mouse location is in
+ * @return the offset at the specified x location in the specified line,
+ * relative to the beginning of the document
+ */
+int getOffsetAtMouseLocation(int x, int line) {
+ String lineText = content.getLine(line);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x);
+ return lineOffset + offsetInLine;
+}
+/**
+ * Returns the offset of the character at the given x location in the line.
+ * <p>
+ *
+ * @param line text of the line to calculate the offset in
+ * @param lineOffset offset of the first character in the line.
+ * 0 based from the beginning of the document.
+ * @param lineXOffset x location in the line
+ * @return offset of the character at the x location relative to the start
+ * of the line. -1 if the x location is past the end if the line.
+ */
+int getOffsetAtX(String line, int lineOffset, int lineXOffset) {
+ GC gc = getGC();
+ int offset;
+
+ lineXOffset += (horizontalScrollOffset - leftMargin);
+ if (isBidi()) {
+ StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc);
+ offset = bidi.getOffsetAtX(lineXOffset);
+ }
+ else {
+ StyleRange[] styles = null;
+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
+
+ if (event != null) {
+ styles = renderer.filterLineStyles(event.styles);
+ }
+ int low = -1;
+ int high = line.length();
+ while (high - low > 1) {
+ offset = (high + low) / 2;
+ // Restrict right/high search boundary only if x is within searched text segment.
+ // Fixes 1GL4ZVE.
+ if (lineXOffset < renderer.getTextPosition(line, lineOffset, offset + 1, styles, gc)) {
+ high = offset;
+ }
+ else
+ if (high == line.length() && high - offset == 1) {
+ // requested x location is past end of line
+ high = -1;
+ }
+ else {
+ low = offset;
+ }
+ }
+ offset = high;
+ }
+ gc.dispose();
+ return offset;
+}
+/**
+ * Returns the index of the last partially visible line.
+ *
+ * @return index of the last partially visible line.
+ */
+int getPartialBottomIndex() {
+ int partialLineCount = Compatibility.ceil(getClientArea().height, lineHeight);
+ return Math.min(content.getLineCount(), topIndex + partialLineCount) - 1;
+}
+/**
+ * Returns the content in the specified range using the platform line
+ * delimiter to separate lines.
+ * <p>
+ *
+ * @param writer the TextWriter to write line text into
+ * @return the content in the specified range using the platform line
+ * delimiter to separate lines as written by the specified TextWriter.
+ */
+String getPlatformDelimitedText(TextWriter writer) {
+ int end = writer.getStart() + writer.getCharCount();
+ int startLine = logicalContent.getLineAtOffset(writer.getStart());
+ int endLine = logicalContent.getLineAtOffset(end);
+ String endLineText = logicalContent.getLine(endLine);
+ int endLineOffset = logicalContent.getOffsetAtLine(endLine);
+
+ for (int i = startLine; i <= endLine; i++) {
+ writer.writeLine(logicalContent.getLine(i), logicalContent.getOffsetAtLine(i));
+ if (i < endLine) {
+ writer.writeLineDelimiter(PlatformLineDelimiter);
+ }
+ }
+ if (end > endLineOffset + endLineText.length()) {
+ writer.writeLineDelimiter(PlatformLineDelimiter);
+ }
+ writer.close();
+ return writer.toString();
+}
+/**
+ * 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,
+ * ranging from 0..N
+ * <p>
+ *
+ * @return start and end of the selection, x is the offset of the first
+ * selected character, y is the offset after the last selected character.
+ * The selection values returned are visual (i.e., x will always always be
+ * <= y). To determine if a selection is right-to-left (RtoL) vs. left-to-right
+ * (LtoR), compare the caretOffset to the start and end of the selection
+ * (e.g., caretOffset == start of selection implies that the selection is RtoL).
+ * @see #getSelectionRange
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSelection() {
+ checkWidget();
+ return new Point(selection.x, selection.y);
+}
+/**
+ * Returns the selection.
+ * <p>
+ *
+ * @return start and length of the selection, x is the offset of the
+ * first selected character, relative to the first character of the
+ * widget content. y is the length of the selection.
+ * The selection values returned are visual (i.e., length will always always be
+ * positive). To determine if a selection is right-to-left (RtoL) vs. left-to-right
+ * (LtoR), compare the caretOffset to the start and end of the selection
+ * (e.g., caretOffset == start of selection implies that the selection is RtoL).
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSelectionRange() {
+ checkWidget();
+ return new Point(selection.x, selection.y - selection.x);
+}
+/**
+ * Returns the receiver's selection background color.
+ *
+ * @return the selection background color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 2.1
+ */
+public Color getSelectionBackground() {
+ checkWidget();
+ if (selectionBackground == null) {
+ return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
+ }
+ return selectionBackground;
+}
+/**
+ * Gets the number of selected characters.
+ * <p>
+ *
+ * @return the number of selected characters.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount() {
+ checkWidget();
+ return getSelectionRange().y;
+}
+/**
+ * Returns the receiver's selection foreground color.
+ *
+ * @return the selection foreground color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 2.1
+ */
+public Color getSelectionForeground() {
+ checkWidget();
+ if (selectionForeground == null) {
+ return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
+ }
+ return selectionForeground;
+}
+/**
+ * Returns the selected text.
+ * <p>
+ *
+ * @return selected text, or an empty String if there is no selection.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getSelectionText() {
+ checkWidget();
+ return content.getTextRange(selection.x, selection.y - selection.x);
+}
+/**
+ * Returns the text segments that should be treated as if they
+ * had a different direction than the surrounding text.
+ * <p>
+ *
+ * @param lineOffset offset of the first character in the line.
+ * 0 based from the beginning of the document.
+ * @param line text of the line to specify bidi segments for
+ * @return text segments that should be treated as if they had a
+ * different direction than the surrounding text. Only the start
+ * index of a segment is specified, relative to the start of the
+ * line. Always starts with 0 and ends with the line length.
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
+ * by the listener do not start with 0, are not in ascending order,
+ * exceed the line length or have duplicates</li>
+ * </ul>
+ */
+int [] getBidiSegments(int lineOffset, String line) {
+ if (isListening(LineGetSegments) == false) {
+ return getBidiSegmentsCompatibility(line, lineOffset);
+ }
+ StyledTextEvent event = sendLineEvent(LineGetSegments, lineOffset, line);
+ int lineLength = line.length();
+ int[] segments;
+ if (event == null || event.segments == null || event.segments.length == 0) {
+ segments = new int[] {0, lineLength};
+ }
+ else {
+ int segmentCount = event.segments.length;
+
+ // test segment index consistency
+ if (event.segments[0] != 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ for (int i = 1; i < segmentCount; i++) {
+ if (event.segments[i] <= event.segments[i - 1] || event.segments[i] > lineLength) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ }
+ // ensure that last segment index is line end offset
+ if (event.segments[segmentCount - 1] != lineLength) {
+ segments = new int[segmentCount + 1];
+ System.arraycopy(event.segments, 0, segments, 0, segmentCount);
+ segments[segmentCount] = lineLength;
+ }
+ else {
+ segments = event.segments;
+ }
+ }
+ return segments;
+}
+/**
+ * @see getBidiSegments
+ * Supports deprecated setBidiColoring API. Remove when API is removed.
+ */
+int [] getBidiSegmentsCompatibility(String line, int lineOffset) {
+ StyledTextEvent event;
+ StyleRange [] styles = new StyleRange [0];
+ int lineLength = line.length();
+ if (bidiColoring == false) {
+ return new int[] {0, lineLength};
+ }
+ event = renderer.getLineStyleData(lineOffset, line);
+ if (event != null) {
+ styles = event.styles;
+ }
+ if (styles.length == 0) {
+ return new int[] {0, lineLength};
+ }
+ int k=0, count = 1;
+ while (k < styles.length && styles[k].start == 0 && styles[k].length == lineLength) {
+ k++;
+ }
+ int[] offsets = new int[(styles.length - k) * 2 + 2];
+ for (int i = k; i < styles.length; i++) {
+ StyleRange style = styles[i];
+ int styleLineStart = Math.max(style.start - lineOffset, 0);
+ int styleLineEnd = Math.max(style.start + style.length - lineOffset, styleLineStart);
+ styleLineEnd = Math.min (styleLineEnd, line.length ());
+ if (i > 0 && count > 1 &&
+ ((styleLineStart >= offsets[count-2] && styleLineStart <= offsets[count-1]) ||
+ (styleLineEnd >= offsets[count-2] && styleLineEnd <= offsets[count-1])) &&
+ style.similarTo(styles[i-1])) {
+ offsets[count-2] = Math.min(offsets[count-2], styleLineStart);
+ offsets[count-1] = Math.max(offsets[count-1], styleLineEnd);
+ } else {
+ if (styleLineStart > offsets[count - 1]) {
+ offsets[count] = styleLineStart;
+ count++;
+ }
+ offsets[count] = styleLineEnd;
+ count++;
+ }
+ }
+ // add offset for last non-colored segment in line, if any
+ if (lineLength > offsets[count-1]) {
+ offsets [count] = lineLength;
+ count++;
+ }
+ if (count == offsets.length) {
+ return offsets;
+ }
+ int [] result = new int [count];
+ System.arraycopy (offsets, 0, result, 0, count);
+ return result;
+}
+/**
+ * Returns the style range at the given offset.
+ * Returns null if a LineStyleListener has been set or if a style is not set
+ * for the offset.
+ * Should not be called if a LineStyleListener has been set since the
+ * listener maintains the styles.
+ * <p>
+ *
+ * @param offset the offset to return the style for.
+ * 0 <= offset < getCharCount() must be true.
+ * @return a StyleRange with start == offset and length == 1, indicating
+ * the style at the given offset. null if a LineStyleListener has been set
+ * or if a style is not set for the given offset.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when the offset is invalid</li>
+ * </ul>
+ */
+public StyleRange getStyleRangeAtOffset(int offset) {
+ checkWidget();
+ if (offset < 0 || offset >= getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (userLineStyle == false) {
+ return defaultLineStyler.getStyleRangeAtOffset(offset);
+ }
+ return null;
+}
+/**
+ * Returns the styles.
+ * Returns an empty array if a LineStyleListener has been set.
+ * Should not be called if a LineStyleListener has been set since the
+ * listener maintains the styles.
+ * <p>
+ *
+ * @return the styles or null if a LineStyleListener has been set.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public StyleRange [] getStyleRanges() {
+ checkWidget();
+ StyleRange styles[];
+
+ if (userLineStyle == false) {
+ styles = defaultLineStyler.getStyleRanges();
+ }
+ else {
+ styles = new StyleRange[0];
+ }
+ return styles;
+}
+/**
+ * Returns a StyledTextBidi object for the specified line.
+ * <p>
+ *
+ * @param lineText the line that the StyledTextBidi object should
+ * work on.
+ * @param lineOffset offset of the beginning of the line, relative
+ * to the beginning of the document
+ * @param gc GC to use when creating a new StyledTextBidi object.
+ * @return a StyledTextBidi object for the specified line.
+ */
+StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc) {
+ return getStyledTextBidi(lineText, lineOffset, gc, null);
+}
+/**
+ * Returns a StyledTextBidi object for the specified line.
+ * <p>
+ *
+ * @param lineText the line that the StyledTextBidi object should
+ * work on.
+ * @param lineOffset offset of the beginning of the line, relative
+ * to the beginning of the document
+ * @param gc GC to use when creating a new StyledTextBidi object.
+ * @param styles StyleRanges to use when creating a new StyledTextBidi
+ * object.
+ * @return a StyledTextBidi object for the specified line.
+ */
+StyledTextBidi getStyledTextBidi(String lineText, int lineOffset, GC gc, StyleRange[] styles) {
+ return renderer.getStyledTextBidi(lineText, lineOffset, gc, styles);
+}
+/**
+ * Returns the tab width measured in characters.
+ *
+ * @return tab width measured in characters
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTabs() {
+ checkWidget();
+ return tabLength;
+}
+/**
+ * Returns a copy of the widget content.
+ * <p>
+ *
+ * @return copy of the widget content
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText() {
+ checkWidget();
+ return content.getTextRange(0, getCharCount());
+}
+/**
+ * Returns the widget content between the two offsets.
+ * <p>
+ *
+ * @param start offset of the first character in the returned String
+ * @param end offset of the last character in the returned String
+ * @return widget content starting at start and ending at end
+ * @see #getTextRange(int,int)
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
+ * </ul>
+ */
+public String getText(int start, int end) {
+ checkWidget();
+ int contentLength = getCharCount();
+
+ if (start < 0 || start >= contentLength || end < 0 || end >= contentLength || start > end) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ return content.getTextRange(start, end - start + 1);
+}
+/**
+ * Returns the widget content starting at start for length characters.
+ * <p>
+ *
+ * @param start offset of the first character in the returned String
+ * @param length number of characters to return
+ * @return widget content starting at start and extending length characters.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when start and/or length are outside the widget content</li>
+ * </ul>
+ */
+public String getTextRange(int start, int length) {
+ checkWidget();
+ int contentLength = getCharCount();
+ int end = start + length;
+
+ if (start > end || start < 0 || end > contentLength) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ return content.getTextRange(start, length);
+}
+/**
+ * Gets the text limit. The text limit specifies the amount of text that the user
+ * can type into the widget.
+ * <p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTextLimit() {
+ checkWidget();
+
+ return textLimit;
+}
+/**
+ * Returns the x position of the character at the specified offset
+ * relative to the first character in the line.
+ * Expands tabs to tab stops using the widget tab width.
+ * <p>
+ *
+ * @param line line to be measured.
+ * @param lineIndex index of the line relative to the first kine of the
+ * document
+ * @param length number of characters to measure. Tabs are counted
+ * as one character in this parameter.
+ * @param gc GC to use for measuring text
+ * @return x position of the character at the specified offset
+ * with tabs expanded to tab stops. 0 if the length is outside the
+ * specified text.
+ */
+int getTextPosition(String line, int lineIndex, int length, GC gc) {
+ int lineOffset = content.getOffsetAtLine(lineIndex);
+ int lineLength = line.length();
+ int width;
+ if (lineLength == 0 || length > lineLength) {
+ return 0;
+ }
+ if (isBidi()) {
+ StyledTextBidi bidi = getStyledTextBidi(line, lineOffset, gc, null);
+ width = getBidiTextPosition(line, length, bidi);
+ }
+ else {
+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
+ StyleRange[] styles = null;
+ if (event != null) {
+ styles = renderer.filterLineStyles(event.styles);
+ }
+ width = renderer.getTextPosition(line, lineOffset, length, styles, gc);
+ }
+ return width;
+}
+/**
+ * Gets the top index. The top index is the index of the fully visible line that
+ * is currently at the top of the widget or the topmost partially visible line if
+ * no line is fully visible.
+ * The top index changes when the widget is scrolled. Indexing is zero based.
+ * <p>
+ *
+ * @return the index of the top line
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopIndex() {
+ checkWidget();
+ int logicalTopIndex = topIndex;
+
+ if (wordWrap) {
+ int visualLineOffset = content.getOffsetAtLine(topIndex);
+ logicalTopIndex = logicalContent.getLineAtOffset(visualLineOffset);
+ }
+ return logicalTopIndex;
+}
+/**
+ * Gets the top pixel. The top pixel is the pixel position of the line that is
+ * currently at the top of the widget.The text widget can be scrolled by pixels
+ * by dragging the scroll thumb so that a partial line may be displayed at the top
+ * the widget. The top pixel changes when the widget is scrolled. The top pixel
+ * does not include the widget trimming.
+ * <p>
+ *
+ * @return pixel position of the top line
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopPixel() {
+ checkWidget();
+ return verticalScrollOffset;
+}
+/**
+ * Returns the vertical scroll increment.
+ * <p>
+ *
+ * @return vertical scroll increment.
+ */
+int getVerticalIncrement() {
+ return lineHeight;
+}
+/**
+ * Returns the index of the line the caret is on.
+ * When in word wrap mode and at the end of one wrapped line/
+ * beginning of the continuing wrapped line the caret offset
+ * is not sufficient to determine the caret line.
+ *
+ * @return the index of the line the caret is on.
+ */
+int getCaretLine() {
+ int caretLine = content.getLineAtOffset(caretOffset);
+ int leftColumnX = 0;
+
+ if (isBidi()) {
+ leftColumnX = XINSET;
+ }
+ if (wordWrap && columnX <= leftColumnX &&
+ caretLine < content.getLineCount() - 1 &&
+ caretOffset == content.getOffsetAtLine(caretLine + 1)) {
+ caretLine++;
+ }
+ return caretLine;
+}
+/**
+ * Returns the offset of the character after the word at the specified
+ * offset.
+ * <p>
+ * There are two classes of words formed by a sequence of characters:
+ * <ul>
+ * <li>from 0-9 and A-z (ASCII 48-57 and 65-122)
+ * <li>every other character except line breaks
+ * </ul>
+ * </p>
+ * <p>
+ * Space characters ' ' (ASCII 20) are special as they are treated as
+ * part of the word leading up to the space character. Line breaks are
+ * treated as one word.
+ * </p>
+ */
+int getWordEnd(int offset) {
+ int line = logicalContent.getLineAtOffset(offset);
+ int lineOffset = logicalContent.getOffsetAtLine(line);
+ String lineText = logicalContent.getLine(line);
+ int lineLength = lineText.length();
+
+ if (offset >= getCharCount()) {
+ return offset;
+ }
+ if (offset == lineOffset + lineLength) {
+ line++;
+ offset = logicalContent.getOffsetAtLine(line);
+ }
+ else {
+ offset -= lineOffset;
+ char ch = lineText.charAt(offset);
+ boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
+ while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit) {
+ offset++;
+ ch = lineText.charAt(offset);
+ }
+ // skip over trailing whitespace
+ while (offset < lineLength - 1 && Compatibility.isSpaceChar(ch)) {
+ offset++;
+ ch = lineText.charAt(offset);
+ }
+ if (offset == lineLength - 1 && (Compatibility.isLetterOrDigit(ch) == letterOrDigit || Compatibility.isSpaceChar(ch))) {
+ offset++;
+ }
+ offset += lineOffset;
+ }
+ return offset;
+}
+/**
+ * Returns the offset of the character after the word at the specified
+ * offset.
+ * <p>
+ * There are two classes of words formed by a sequence of characters:
+ * <ul>
+ * <li>from 0-9 and A-z (ASCII 48-57 and 65-122)
+ * <li>every other character except line breaks
+ * </ul>
+ * </p>
+ * <p>
+ * Spaces are ignored and do not represent a word. Line breaks are treated
+ * as one word.
+ * </p>
+ */
+int getWordEndNoSpaces(int offset) {
+ int line = logicalContent.getLineAtOffset(offset);
+ int lineOffset = logicalContent.getOffsetAtLine(line);
+ String lineText = logicalContent.getLine(line);
+ int lineLength = lineText.length();
+
+ if (offset >= getCharCount()) {
+ return offset;
+ }
+ if (offset == lineOffset + lineLength) {
+ line++;
+ offset = logicalContent.getOffsetAtLine(line);
+ }
+ else {
+ offset -= lineOffset;
+ char ch = lineText.charAt(offset);
+ boolean letterOrDigit = Compatibility.isLetterOrDigit(ch);
+
+ while (offset < lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
+ offset++;
+ ch = lineText.charAt(offset);
+ }
+ if (offset == lineLength - 1 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
+ offset++;
+ }
+ offset += lineOffset;
+ }
+ return offset;
+}
+/**
+ * Returns the start offset of the word at the specified offset.
+ * There are two classes of words formed by a sequence of characters:
+ * <p>
+ * <ul>
+ * <li>from 0-9 and A-z (ASCII 48-57 and 65-122)
+ * <li>every other character except line breaks
+ * </ul>
+ * </p>
+ * <p>
+ * Space characters ' ' (ASCII 20) are special as they are treated as
+ * part of the word leading up to the space character. Line breaks are treated
+ * as one word.
+ * </p>
+ */
+int getWordStart(int offset) {
+ int line = logicalContent.getLineAtOffset(offset);
+ int lineOffset = logicalContent.getOffsetAtLine(line);
+ String lineText = logicalContent.getLine(line);
+
+ if (offset <= 0) {
+ return offset;
+ }
+ if (offset == lineOffset) {
+ line--;
+ lineText = logicalContent.getLine(line);
+ offset = logicalContent.getOffsetAtLine(line) + lineText.length();
+ }
+ else {
+ char ch;
+ boolean letterOrDigit;
+
+ offset -= lineOffset;
+ // skip over trailing whitespace
+ do {
+ offset--;
+ ch = lineText.charAt(offset);
+ } while (offset > 0 && Compatibility.isSpaceChar(ch));
+ letterOrDigit = Compatibility.isLetterOrDigit(ch);
+ while (offset > 0 && Compatibility.isLetterOrDigit(ch) == letterOrDigit && Compatibility.isSpaceChar(ch) == false) {
+ offset--;
+ ch = lineText.charAt(offset);
+ }
+ if (offset > 0 || Compatibility.isLetterOrDigit(ch) != letterOrDigit) {
+ offset++;
+ }
+ offset += lineOffset;
+ }
+ return offset;
+}
+/**
+ * Returns whether the widget wraps lines.
+ * <p>
+ *
+ * @return true if widget wraps lines, false otherwise
+ * @since 2.0
+ */
+public boolean getWordWrap() {
+ checkWidget();
+ return wordWrap;
+}
+/**
+ * Returns the x location of the character at the give offset in the line.
+ * <b>NOTE:</b> Does not return correct values for true italic fonts (vs. slanted fonts).
+ * <p>
+ *
+ * @return x location of the character at the given offset in the line.
+ */
+int getXAtOffset(String line, int lineIndex, int lineOffset) {
+ int x;
+ if (lineOffset == 0 && isBidi() == false) {
+ x = leftMargin;
+ }
+ else {
+ GC gc = getGC();
+ x = getTextPosition(line, lineIndex, Math.min(line.length(), lineOffset), gc) + leftMargin;
+ gc.dispose();
+ if (lineOffset > line.length()) {
+ // offset is not on the line. return an x location one character
+ // after the line to indicate the line delimiter.
+ x += lineEndSpaceWidth;
+ }
+ }
+ return x - horizontalScrollOffset;
+}
+/**
+ * Inserts a string. The old selection is replaced with the new text.
+ * <p>
+ *
+ * @param string the string
+ * @see #replaceTextRange(int,int,String)
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when string is null</li>
+ * </ul>
+ */
+public void insert(String string) {
+ checkWidget();
+ if (string == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ Point sel = getSelectionRange();
+ replaceTextRange(sel.x, sel.y, string);
+}
+/**
+ * Creates content change listeners and set the default content model.
+ */
+void installDefaultContent() {
+ textChangeListener = new TextChangeListener() {
+ public void textChanging(TextChangingEvent event) {
+ handleTextChanging(event);
+ }
+ public void textChanged(TextChangedEvent event) {
+ handleTextChanged(event);
+ }
+ public void textSet(TextChangedEvent event) {
+ handleTextSet(event);
+ }
+ };
+ logicalContent = content = new DefaultContent();
+ content.addTextChangeListener(textChangeListener);
+}
+/**
+ * Creates a default line style listener.
+ * Used to store line background colors and styles.
+ * Removed when the user sets a LineStyleListener.
+ * <p>
+ *
+ * @see #addLineStyleListener
+ */
+void installDefaultLineStyler() {
+ defaultLineStyler = new DefaultLineStyler(logicalContent);
+ StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
+ if (userLineStyle == false) {
+ addListener(LineGetStyle, typedListener);
+ }
+ if (userLineBackground == false) {
+ addListener(LineGetBackground, typedListener);
+ }
+}
+/**
+ * Adds event listeners
+ */
+void installListeners() {
+ ScrollBar verticalBar = getVerticalBar();
+ ScrollBar horizontalBar = getHorizontalBar();
+
+ addListener(SWT.Dispose, new Listener() {
+ public void handleEvent(Event event) {
+ handleDispose();
+ }
+ });
+ addListener(SWT.KeyDown, new Listener() {
+ public void handleEvent(Event event) {
+ handleKeyDown(event);
+ }
+ });
+ addListener(SWT.MouseDown, new Listener() {
+ public void handleEvent(Event event) {
+ handleMouseDown(event);
+ }
+ });
+ addListener(SWT.MouseUp, new Listener() {
+ public void handleEvent(Event event) {
+ handleMouseUp(event);
+ }
+ });
+ addListener(SWT.MouseDoubleClick, new Listener() {
+ public void handleEvent(Event event) {
+ handleMouseDoubleClick(event);
+ }
+ });
+ addListener(SWT.MouseMove, new Listener() {
+ public void handleEvent(Event event) {
+ handleMouseMove(event);
+ }
+ });
+ addListener(SWT.Paint, new Listener() {
+ public void handleEvent(Event event) {
+ handlePaint(event);
+ }
+ });
+ addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event event) {
+ handleResize(event);
+ }
+ });
+ addListener(SWT.Traverse, new Listener() {
+ public void handleEvent(Event event) {
+ handleTraverse(event);
+ }
+ });
+ if (verticalBar != null) {
+ verticalBar.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ handleVerticalScroll(event);
+ }
+ });
+ }
+ if (horizontalBar != null) {
+ horizontalBar.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ handleHorizontalScroll(event);
+ }
+ });
+ }
+}
+StyledTextContent internalGetContent() {
+ return content;
+}
+int internalGetHorizontalPixel() {
+ return horizontalScrollOffset;
+}
+LineCache internalGetLineCache() {
+ return lineCache;
+}
+Point internalGetSelection() {
+ return selection;
+}
+boolean internalGetWordWrap() {
+ return wordWrap;
+}
+/**
+ * Used by WordWrapCache to bypass StyledText.redraw which does
+ * an unwanted cache reset.
+ */
+void internalRedraw() {
+ super.redraw();
+}
+/**
+ * Redraws the specified text range.
+ * <p>
+ *
+ * @param start offset of the first character to redraw
+ * @param length number of characters to redraw
+ * @param clearBackground true if the background should be cleared as
+ * part of the redraw operation. If true, the entire redraw range will
+ * be cleared before anything is redrawn. If the redraw range includes
+ * the last character of a line (i.e., the entire line is redrawn) the
+ * line is cleared all the way to the right border of the widget.
+ * The redraw operation will be faster and smoother if clearBackground is
+ * set to false. Whether or not the flag can be set to false depends on
+ * the type of change that has taken place. If font styles or background
+ * colors for the redraw range have changed, clearBackground should be
+ * set to true. If only foreground colors have changed for the redraw
+ * range, clearBackground can be set to false.
+ */
+void internalRedrawRange(int start, int length, boolean clearBackground) {
+ int end = start + length;
+ int firstLine = content.getLineAtOffset(start);
+ int lastLine = content.getLineAtOffset(end);
+ int offsetInFirstLine;
+ int partialBottomIndex = getPartialBottomIndex();
+ int partialTopIndex = verticalScrollOffset / lineHeight;
+ // do nothing if redraw range is completely invisible
+ if (firstLine > partialBottomIndex || lastLine < partialTopIndex) {
+ return;
+ }
+ // only redraw visible lines
+ if (partialTopIndex > firstLine) {
+ firstLine = partialTopIndex;
+ offsetInFirstLine = 0;
+ }
+ else {
+ offsetInFirstLine = start - content.getOffsetAtLine(firstLine);
+ }
+ if (partialBottomIndex + 1 < lastLine) {
+ lastLine = partialBottomIndex + 1; // + 1 to redraw whole bottom line, including line break
+ end = content.getOffsetAtLine(lastLine);
+ }
+ // redraw first and last lines
+ if (isBidi()) {
+ redrawBidiLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
+ }
+ else {
+ redrawLines(firstLine, offsetInFirstLine, lastLine, end, clearBackground);
+ }
+ // redraw entire center lines if redraw range includes more than two lines
+ if (lastLine - firstLine > 1) {
+ Rectangle clientArea = getClientArea();
+ int redrawStopY = lastLine * lineHeight - verticalScrollOffset;
+ int redrawY = (firstLine + 1) * lineHeight - verticalScrollOffset;
+ draw(0, redrawY, clientArea.width, redrawStopY - redrawY, clearBackground);
+ }
+}
+/**
+ * Returns the widget text with style information encoded using RTF format
+ * specification version 1.5.
+ *
+ * @return the widget text with style information encoded using RTF format
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+String getRtf(){
+ checkWidget();
+ RTFWriter rtfWriter = new RTFWriter(0, getCharCount());
+ return getPlatformDelimitedText(rtfWriter);
+}
+/**
+ * Frees resources.
+ */
+void handleDispose() {
+ clipboard.dispose();
+ ibeamCursor.dispose();
+ if (renderer != null) {
+ renderer.dispose();
+ renderer = null;
+ }
+ if (content != null) {
+ content.removeTextChangeListener(textChangeListener);
+ }
+ if (leftCaretBitmap != null) {
+ leftCaretBitmap.dispose();
+ leftCaretBitmap = null;
+ }
+ if (rightCaretBitmap != null) {
+ rightCaretBitmap.dispose();
+ rightCaretBitmap = null;
+ }
+ if (isBidi()) {
+ StyledTextBidi.removeLanguageListener(this);
+ }
+}
+/**
+ * Scrolls the widget horizontally.
+ */
+void handleHorizontalScroll(Event event) {
+ int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
+ scrollHorizontal(scrollPixel);
+}
+/**
+ * If an action has been registered for the key stroke execute the action.
+ * Otherwise, if a character has been entered treat it as new content.
+ * <p>
+ *
+ * @param event keyboard event
+ */
+void handleKey(Event event) {
+ int action;
+
+ if (event.keyCode != 0) {
+ // special key pressed (e.g., F1)
+ action = getKeyBinding(event.keyCode | event.stateMask);
+ }
+ else {
+ // character key pressed
+ action = getKeyBinding(event.character | event.stateMask);
+ if (action == SWT.NULL) {
+ // see if we have a control character
+ if ((event.stateMask & SWT.CTRL) != 0 && (event.character >= 0) && event.character <= 31) {
+ // get the character from the CTRL+char sequence, the control
+ // key subtracts 64 from the value of the key that it modifies
+ int c = event.character + 64;
+ action = getKeyBinding(c | event.stateMask);
+ }
+ }
+ }
+ if (action == SWT.NULL) {
+ boolean ignore = false;
+
+ if (isCarbon) {
+ // Ignore acclerator key combinations (we do not want to
+ // insert a character in the text in this instance). Do not
+ // ignore COMMAND+ALT combinations since that key sequence
+ // produces characters on the mac.
+ ignore = (event.stateMask ^ SWT.COMMAND) == 0 ||
+ (event.stateMask ^ (SWT.COMMAND | SWT.SHIFT)) == 0;
+ } else {
+ // Ignore acclerator key combinations (we do not want to
+ // insert a character in the text in this instance). Don't
+ // ignore CTRL+ALT combinations since that is the Alt Gr
+ // key on some keyboards. See bug 20953.
+ ignore = (event.stateMask ^ SWT.ALT) == 0 ||
+ (event.stateMask ^ SWT.CTRL) == 0 ||
+ (event.stateMask ^ (SWT.ALT | SWT.SHIFT)) == 0 ||
+ (event.stateMask ^ (SWT.CTRL | SWT.SHIFT)) == 0;
+ }
+ // -ignore anything below SPACE except for line delimiter keys and tab.
+ // -ignore DEL
+ if (!ignore && event.character > 31 && event.character != SWT.DEL ||
+ event.character == SWT.CR || event.character == SWT.LF ||
+ event.character == TAB) {
+ doContent(event.character);
+ }
+ }
+ else {
+ invokeAction(action);
+ }
+}
+/**
+ * If a VerifyKey listener exists, verify that the key that was entered
+ * should be processed.
+ * <p>
+ *
+ * @param event keyboard event
+ */
+void handleKeyDown(Event event) {
+ Event verifyEvent = new Event();
+
+ verifyEvent.character = event.character;
+ verifyEvent.keyCode = event.keyCode;
+ verifyEvent.stateMask = event.stateMask;
+ verifyEvent.doit = true;
+ notifyListeners(VerifyKey, verifyEvent);
+ if (verifyEvent.doit == true) {
+ handleKey(event);
+ }
+}
+/**
+ * Updates the caret location and selection if mouse button 1 has been
+ * pressed.
+ */
+void handleMouseDoubleClick(Event event) {
+ if (event.button != 1 || doubleClickEnabled == false) {
+ return;
+ }
+ event.y -= topMargin;
+ mouseDoubleClick = true;
+ caretOffset = getWordStart(caretOffset);
+ resetSelection();
+ caretOffset = getWordEndNoSpaces(caretOffset);
+ showCaret();
+ doMouseSelection();
+ doubleClickSelection = new Point(selection.x, selection.y);
+}
+/**
+ * Updates the caret location and selection if mouse button 1 has been
+ * pressed.
+ */
+void handleMouseDown(Event event) {
+ boolean select = (event.stateMask & SWT.MOD2) != 0;
+
+ if (event.button != 1) {
+ return;
+ }
+ mouseDoubleClick = false;
+ event.y -= topMargin;
+ doMouseLocationChange(event.x, event.y, select);
+}
+/**
+ * Updates the caret location and selection if mouse button 1 is pressed
+ * during the mouse move.
+ */
+void handleMouseMove(Event event) {
+ if ((event.stateMask & SWT.BUTTON1) == 0) {
+ return;
+ }
+ event.y -= topMargin;
+ doMouseLocationChange(event.x, event.y, true);
+ doAutoScroll(event);
+}
+/**
+ * Autoscrolling ends when the mouse button is released.
+ */
+void handleMouseUp(Event event) {
+ event.y -= topMargin;
+ endAutoScroll();
+}
+/**
+ * Renders the invalidated area specified in the paint event.
+ * <p>
+ *
+ * @param event paint event
+ */
+void handlePaint(Event event) {
+ int startLine = Math.max(0, (event.y - topMargin + verticalScrollOffset) / lineHeight);
+ int paintYFromTopLine = (startLine - topIndex) * lineHeight;
+ int topLineOffset = topIndex * lineHeight - verticalScrollOffset;
+ int startY = paintYFromTopLine + topLineOffset + topMargin; // adjust y position for pixel based scrolling and top margin
+ int renderHeight = event.y + event.height - startY;
+ Rectangle clientArea = getClientArea();
+
+ // Check if there is work to do. clientArea.width should never be 0
+ // if we receive a paint event but we never want to try and create
+ // an Image with 0 width.
+ if (clientArea.width == 0 || event.height == 0) {
+ return;
+ }
+ performPaint(event.gc, startLine, startY, renderHeight);
+}
+/**
+ * Recalculates the scroll bars. Rewraps all lines when in word
+ * wrap mode.
+ * <p>
+ *
+ * @param event resize event
+ */
+void handleResize(Event event) {
+ int oldHeight = clientAreaHeight;
+ int oldWidth = clientAreaWidth;
+
+ clientAreaHeight = getClientArea().height;
+ clientAreaWidth = getClientArea().width;
+ if (wordWrap) {
+ if (oldWidth != clientAreaWidth) {
+ wordWrapResize(oldWidth);
+ }
+ }
+ else
+ if (clientAreaHeight > oldHeight) {
+ int lineCount = content.getLineCount();
+ int oldBottomIndex = topIndex + oldHeight / lineHeight;
+ int newItemCount = Compatibility.ceil(clientAreaHeight - oldHeight, lineHeight);
+
+ oldBottomIndex = Math.min(oldBottomIndex, lineCount);
+ newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex);
+ lineCache.calculate(oldBottomIndex, newItemCount);
+ }
+ setScrollBars();
+ claimBottomFreeSpace();
+ claimRightFreeSpace();
+ if (oldHeight != clientAreaHeight) {
+ calculateTopIndex();
+ }
+}
+/**
+ * Updates the caret position and selection and the scroll bars to reflect
+ * the content change.
+ * <p>
+ */
+void handleTextChanged(TextChangedEvent event) {
+ lineCache.textChanged(lastTextChangeStart,
+ lastTextChangeNewLineCount,
+ lastTextChangeReplaceLineCount,
+ lastTextChangeNewCharCount,
+ lastTextChangeReplaceCharCount);
+ setScrollBars();
+ // update selection/caret location after styles have been changed.
+ // otherwise any text measuring could be incorrect
+ //
+ // also, this needs to be done after all scrolling. Otherwise,
+ // selection redraw would be flushed during scroll which is wrong.
+ // in some cases new text would be drawn in scroll source area even
+ // though the intent is to scroll it.
+ // fixes 1GB93QT
+ updateSelection(
+ lastTextChangeStart,
+ lastTextChangeReplaceCharCount,
+ lastTextChangeNewCharCount);
+
+ if (lastTextChangeReplaceLineCount > 0) {
+ // Only check for unused space when lines are deleted.
+ // Fixes 1GFL4LY
+ // Scroll up so that empty lines below last text line are used.
+ // Fixes 1GEYJM0
+ claimBottomFreeSpace();
+ }
+ if (lastTextChangeReplaceCharCount > 0) {
+ // fixes bug 8273
+ claimRightFreeSpace();
+ }
+ // do direct drawing if the text change is confined to a single line.
+ // optimization and fixes bug 13999. see also handleTextChanging.
+ if (lastTextChangeNewLineCount == 0 && lastTextChangeReplaceLineCount == 0) {
+ int startLine = content.getLineAtOffset(lastTextChangeStart);
+ int startY = startLine * lineHeight - verticalScrollOffset + topMargin;
+
+ GC gc = getGC();
+ Caret caret = getCaret();
+ boolean caretVisible = false;
+
+ if (caret != null) {
+ caretVisible = caret.getVisible();
+ caret.setVisible(false);
+ }
+ performPaint(gc, startLine, startY, lineHeight);
+ if (caret != null) {
+ caret.setVisible(caretVisible);
+ }
+ gc.dispose();
+ }
+}
+/**
+ * Updates the screen to reflect a pending content change.
+ * <p>
+ *
+ * @param event.start the start offset of the change
+ * @param event.newText text that is going to be inserted or empty String
+ * if no text will be inserted
+ * @param event.replaceCharCount length of text that is going to be replaced
+ * @param event.newCharCount length of text that is going to be inserted
+ * @param event.replaceLineCount number of lines that are going to be replaced
+ * @param event.newLineCount number of new lines that are going to be inserted
+ */
+void handleTextChanging(TextChangingEvent event) {
+ int firstLine;
+ int textChangeY;
+ boolean isMultiLineChange = event.replaceLineCount > 0 || event.newLineCount > 0;
+
+ if (event.replaceCharCount < 0) {
+ event.start += event.replaceCharCount;
+ event.replaceCharCount *= -1;
+ }
+ lastTextChangeStart = event.start;
+ lastTextChangeNewLineCount = event.newLineCount;
+ lastTextChangeNewCharCount = event.newCharCount;
+ lastTextChangeReplaceLineCount = event.replaceLineCount;
+ lastTextChangeReplaceCharCount = event.replaceCharCount;
+ firstLine = content.getLineAtOffset(event.start);
+ textChangeY = firstLine * lineHeight - verticalScrollOffset + topMargin;
+ if (isMultiLineChange) {
+ redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount);
+ }
+ // notify default line styler about text change
+ if (defaultLineStyler != null) {
+ defaultLineStyler.textChanging(event);
+ }
+
+ // Update the caret offset if it is greater than the length of the content.
+ // This is necessary since style range API may be called between the
+ // handleTextChanging and handleTextChanged events and this API sets the
+ // caretOffset.
+ int newEndOfText = content.getCharCount() - event.replaceCharCount + event.newCharCount;
+ if (caretOffset > newEndOfText) caretOffset = newEndOfText;
+}
+/**
+ * Called when the widget content is set programatically, overwriting
+ * the old content. Resets the caret position, selection and scroll offsets.
+ * Recalculates the content width and scroll bars. Redraws the widget.
+ * <p>
+ *
+ * @param event text change event.
+ */
+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
+ * traversal and receive the tab key event instead.
+ * <p>
+ *
+ * @param event the event
+ */
+void handleTraverse(Event event) {
+ int style = getStyle();
+ boolean ignoreTab = (style & SWT.MULTI) != 0 && !editable || isSingleLine();
+
+ if ((event.detail == SWT.TRAVERSE_TAB_NEXT ||
+ event.detail == SWT.TRAVERSE_RETURN) && ignoreTab) {
+ event.doit = true;
+ }
+}
+/**
+ * Scrolls the widget vertically.
+ */
+void handleVerticalScroll(Event event) {
+ setVerticalScrollOffset(getVerticalBar().getSelection(), false);
+}
+/**
+ * Initializes the fonts used to render font styles.
+ * Presently only regular and bold fonts are supported.
+ */
+void initializeRenderer() {
+ if (renderer != null) {
+ renderer.dispose();
+ }
+ renderer = new DisplayRenderer(
+ getDisplay(), getFont(), isBidi(), leftMargin, this, tabLength);
+ lineHeight = renderer.getLineHeight();
+ lineEndSpaceWidth = renderer.getLineEndSpaceWidth();
+}
+/**
+ * Executes the action.
+ * <p>
+ *
+ * @param action one of the actions defined in ST.java
+ */
+public void invokeAction(int action) {
+ int oldColumnX;
+ int caretLine;
+
+ checkWidget();
+ switch (action) {
+ // Navigation
+ case ST.LINE_UP:
+ caretLine = doLineUp();
+ oldColumnX = columnX;
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ // save the original horizontal caret position
+ columnX = oldColumnX;
+ clearSelection(true);
+ break;
+ case ST.LINE_DOWN:
+ caretLine = doLineDown();
+ oldColumnX = columnX;
+ // explicitly go to the calculated caret line. may be different
+ // from content.getLineAtOffset(caretOffset) when in word wrap mode
+ showCaret(caretLine);
+ // save the original horizontal caret position
+ columnX = oldColumnX;
+ clearSelection(true);
+ break;
+ case ST.LINE_START:
+ doLineStart();
+ clearSelection(true);
+ break;
+ case ST.LINE_END:
+ doLineEnd();
+ clearSelection(true);
+ break;
+ case ST.COLUMN_PREVIOUS:
+ doCursorPrevious();
+ clearSelection(true);
+ break;
+ case ST.COLUMN_NEXT:
+ doCursorNext();
+ clearSelection(true);
+ break;
+ case ST.PAGE_UP:
+ doPageUp();
+ clearSelection(true);
+ break;
+ case ST.PAGE_DOWN:
+ doPageDown(false);
+ clearSelection(true);
+ break;
+ case ST.WORD_PREVIOUS:
+ doWordPrevious();
+ clearSelection(true);
+ break;
+ case ST.WORD_NEXT:
+ doWordNext();
+ clearSelection(true);
+ break;
+ case ST.TEXT_START:
+ doContentStart();
+ clearSelection(true);
+ break;
+ case ST.TEXT_END:
+ doContentEnd();
+ clearSelection(true);
+ break;
+ case ST.WINDOW_START:
+ doPageStart();
+ clearSelection(true);
+ break;
+ case ST.WINDOW_END:
+ doPageEnd();
+ clearSelection(true);
+ break;
+ // Selection
+ case ST.SELECT_LINE_UP:
+ doSelectionLineUp();
+ break;
+ case ST.SELECT_LINE_DOWN:
+ doSelectionLineDown();
+ break;
+ case ST.SELECT_LINE_START:
+ doLineStart();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_LINE_END:
+ doLineEnd();
+ doSelection(SWT.RIGHT);
+ break;
+ case ST.SELECT_COLUMN_PREVIOUS:
+ doSelectionCursorPrevious();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_COLUMN_NEXT:
+ doSelectionCursorNext();
+ doSelection(SWT.RIGHT);
+ break;
+ case ST.SELECT_PAGE_UP:
+ doSelectionPageUp();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_PAGE_DOWN:
+ doSelectionPageDown();
+ break;
+ case ST.SELECT_WORD_PREVIOUS:
+ doSelectionWordPrevious();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_WORD_NEXT:
+ doSelectionWordNext();
+ doSelection(SWT.RIGHT);
+ break;
+ case ST.SELECT_TEXT_START:
+ doContentStart();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_TEXT_END:
+ doContentEnd();
+ doSelection(SWT.RIGHT);
+ break;
+ case ST.SELECT_WINDOW_START:
+ doPageStart();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_WINDOW_END:
+ doPageEnd();
+ doSelection(SWT.RIGHT);
+ break;
+ // Modification
+ case ST.CUT:
+ cut();
+ break;
+ case ST.COPY:
+ copy();
+ break;
+ case ST.PASTE:
+ paste();
+ break;
+ case ST.DELETE_PREVIOUS:
+ doBackspace();
+ break;
+ case ST.DELETE_NEXT:
+ doDelete();
+ break;
+ case ST.DELETE_WORD_PREVIOUS:
+ doDeleteWordPrevious();
+ break;
+ case ST.DELETE_WORD_NEXT:
+ doDeleteWordNext();
+ break;
+ // Miscellaneous
+ case ST.TOGGLE_OVERWRITE:
+ overwrite = !overwrite; // toggle insert/overwrite mode
+ break;
+ }
+}
+/**
+ * Temporary until SWT provides this
+ */
+boolean isBidi() {
+ return isBidi;
+}
+/**
+ * Returns whether the given offset is inside a multi byte line delimiter.
+ * Example:
+ * "Line1\r\n" isLineDelimiter(5) == false but isLineDelimiter(6) == true
+ *
+ * @return true if the given offset is inside a multi byte line delimiter.
+ * false if the given offset is before or after a line delimiter.
+ */
+boolean isLineDelimiter(int offset) {
+ int line = content.getLineAtOffset(offset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = offset - lineOffset;
+ // offsetInLine will be greater than line length if the line
+ // delimiter is longer than one character and the offset is set
+ // in between parts of the line delimiter.
+ return offsetInLine > content.getLine(line).length();
+}
+/**
+ * Returns whether or not the given lines are visible.
+ * <p>
+ *
+ * @return true if any of the lines is visible
+ * false if none of the lines is visible
+ */
+boolean isAreaVisible(int firstLine, int lastLine) {
+ int partialBottomIndex = getPartialBottomIndex();
+ int partialTopIndex = verticalScrollOffset / lineHeight;
+ boolean notVisible = firstLine > partialBottomIndex || lastLine < partialTopIndex;
+ return !notVisible;
+}
+/**
+ * Returns whether or not the given styles will necessitate a redraw for the given start line.
+ * A redraw is necessary when font style changes after the start of a style will take place.
+ * This method assumes ranges is in order and non-overlapping.
+ * <p>
+ *
+ * @return true if a redraw of the given line is necessary, false otherwise
+ */
+boolean isRedrawFirstLine(StyleRange[] ranges, int firstLine, int firstLineOffset) {
+ int lineEnd = firstLineOffset + content.getLine(firstLine).length();
+ for (int i=0; i<ranges.length; i++) {
+ StyleRange range = ranges[i];
+ // does the style start on the first line?
+ if (range.start < lineEnd) {
+ int rangeEnd = range.start + range.length;
+ if (isStyleChanging(range, range.start, Math.min(rangeEnd, lineEnd))) return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+/**
+ * Returns whether or not the given styles will necessitate a redraw for the given end line.
+ * A redraw is necessary when font style changes after the start of a style will take place.
+ * This method assumes ranges is in order and non-overlapping.
+ * <p>
+ *
+ * @return true if a redraw of the last line is necessary, false otherwise
+ */
+boolean isRedrawLastLine(StyleRange[] ranges, int lastLine, int lastLineOffset) {
+ for (int i = ranges.length - 1; i >= 0; i--) {
+ StyleRange range = ranges[i];
+ int rangeEnd = range.start + range.length;
+ // does style range end on the last line?
+ if (rangeEnd >= lastLineOffset) {
+ if (isStyleChanging(range, Math.max(range.start, lastLineOffset), rangeEnd)) return true;
+ } else {
+ break;
+ }
+ }
+ return false;
+}
+/**
+ * Returns whether the widget can have only one line.
+ * <p>
+ *
+ * @return true if widget can have only one line, false if widget can have
+ * multiple lines
+ */
+boolean isSingleLine() {
+ return (getStyle() & SWT.SINGLE) != 0;
+}
+/**
+ * Returns whether the font style in the given style range is changing
+ * from SWT.NORMAL to SWT.BOLD or vice versa.
+ * <p>
+ *
+ * @param range StyleRange to compare current font style with.
+ * @param start offset of the first font style to compare
+ * @param end offset behind the last font style to compare
+ * @return true if the font style is changing in the given style range,
+ * false if the font style is not changing in the given style range.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+boolean isStyleChanging(StyleRange range, int start, int end) {
+ checkWidget();
+ StyleRange[] styles = defaultLineStyler.getStyleRangesFor(start, end - start);
+
+ if (styles == null) {
+ return (range.fontStyle != SWT.NORMAL);
+ }
+ for (int i = 0; i < styles.length; i++) {
+ StyleRange newStyle = styles[i];
+ if (newStyle.fontStyle != range.fontStyle) {
+ return true;
+ }
+ }
+ return false;
+}
+/**
+ * Sends the specified verify event, replace/insert text as defined by
+ * the event and send a modify event.
+ * <p>
+ *
+ * @param event the text change event.
+ * <ul>
+ * <li>event.start - the replace start offset</li>
+ * <li>event.end - the replace end offset</li>
+ * <li>event.text - the new text</li>
+ * </ul>
+ * @param updateCaret whether or not he caret should be set behind
+ * the new text
+ */
+void modifyContent(Event event, boolean updateCaret) {
+ event.doit = true;
+ notifyListeners(SWT.Verify, event);
+ if (event.doit) {
+ StyledTextEvent styledTextEvent = null;
+ int replacedLength = event.end - event.start;
+ boolean isCharacterRemove = replacedLength == 1 && event.text.length() == 0;
+ boolean isBackspace = event.start < caretOffset;
+ boolean isDirectionBoundary = false;
+
+ if (updateCaret && isBidi() && isCharacterRemove) {
+ // set the keyboard language to the language of the deleted character.
+ // determine direction boundary so that caret location can be updated
+ // properly.
+ int line = content.getLineAtOffset(caretOffset);
+ int lineStartOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineStartOffset;
+ String lineText = content.getLine(line);
+ GC gc = getGC();
+ StyledTextBidi bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
+ if (isBackspace) {
+ if (offsetInLine > 0) {
+ // the line start/end does not represent a direction boundary
+ // even if the previous/next line has a different direction.
+ isDirectionBoundary =
+ offsetInLine < lineText.length() &&
+ (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) ||
+ bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
+ bidi.setKeyboardLanguage(offsetInLine - 1);
+ }
+ }
+ else {
+ if (offsetInLine < lineText.length()) {
+ // the line start/end does not represent a direction boundary
+ // even if the previous/next line has a different direction.
+ isDirectionBoundary =
+ offsetInLine > 0 &&
+ (bidi.isRightToLeft(offsetInLine) != bidi.isRightToLeft(offsetInLine - 1) ||
+ bidi.isLocalNumber(offsetInLine) != bidi.isLocalNumber(offsetInLine - 1));
+ bidi.setKeyboardLanguage(offsetInLine);
+ }
+ }
+ gc.dispose();
+ }
+ if (isListening(ExtendedModify)) {
+ styledTextEvent = new StyledTextEvent(logicalContent);
+ styledTextEvent.start = event.start;
+ styledTextEvent.end = event.start + event.text.length();
+ styledTextEvent.text = content.getTextRange(event.start, replacedLength);
+ }
+ content.replaceTextRange(event.start, replacedLength, event.text);
+ // set the caret position prior to sending the modify event.
+ // fixes 1GBB8NJ
+ if (updateCaret) {
+ // always update the caret location. fixes 1G8FODP
+ internalSetSelection(event.start + event.text.length(), 0, true);
+ if (isBidi()) {
+ // Update the caret direction so that the caret moves to the
+ // typed/deleted character. Fixes 1GJLQ16.
+ if (isCharacterRemove) {
+ updateBidiDirection(isBackspace, isDirectionBoundary);
+ }
+ else {
+ lastCaretDirection = ST.COLUMN_NEXT;
+ }
+ showBidiCaret();
+ }
+ else {
+ showCaret();
+ }
+ }
+ notifyListeners(SWT.Modify, event);
+ if (isListening(ExtendedModify)) {
+ notifyListeners(ExtendedModify, styledTextEvent);
+ }
+ }
+}
+/**
+ * Replaces the selection with the clipboard text or insert the text at
+ * the current caret offset if there is no selection.
+ * If the widget has the SWT.SINGLE style and the clipboard text contains
+ * more than one line, only the first line without line delimiters is
+ * inserted in the widget.
+ * <p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void paste(){
+ checkWidget();
+ TextTransfer transfer = TextTransfer.getInstance();
+ String text;
+ text = (String) clipboard.getContents(transfer);
+ if (text != null && text.length() > 0) {
+ Event event = new Event();
+ event.start = selection.x;
+ event.end = selection.y;
+ event.text = getModelDelimitedText(text);
+ sendKeyEvent(event);
+ }
+}
+/**
+ * Render the specified area. Broken out as its own method to support
+ * direct drawing.
+ * <p>
+ *
+ * @param gc GC to render on
+ * @param startLine first line to render
+ * @param startY y pixel location to start rendering at
+ * @param renderHeight renderHeight widget area that needs to be filled with lines
+ */
+void performPaint(GC gc,int startLine,int startY, int renderHeight) {
+ Rectangle clientArea = getClientArea();
+ Color background = getBackground();
+
+ // Check if there is work to do. We never want to try and create
+ // an Image with 0 width or 0 height.
+ if (clientArea.width == 0) {
+ return;
+ }
+ if (renderHeight > 0) {
+ // renderHeight will be negative when only top margin needs redrawing
+ Color foreground = getForeground();
+ int lineCount = content.getLineCount();
+ int paintY = 0;
+
+ if (isSingleLine()) {
+ lineCount = 1;
+ if (startLine > 1) {
+ startLine = 1;
+ }
+ }
+ Image lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight);
+ GC lineGC = new GC(lineBuffer);
+
+ lineGC.setFont(getFont());
+ renderer.setCurrentFontStyle(SWT.NORMAL);
+ lineGC.setForeground(foreground);
+ lineGC.setBackground(background);
+
+ for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) {
+ String line = content.getLine(i);
+ renderer.drawLine(line, i, paintY, lineGC, background, foreground, true);
+ }
+ if (paintY < renderHeight) {
+ lineGC.setBackground(background);
+ lineGC.setForeground(background);
+ lineGC.fillRectangle(0, paintY, clientArea.width, renderHeight - paintY);
+ }
+ gc.drawImage(lineBuffer, 0, startY);
+ lineGC.dispose();
+ lineBuffer.dispose();
+ }
+ clearMargin(gc, background, clientArea, renderHeight);
+}
+/**
+ * Prints the widget's text to the default printer.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void print() {
+ checkWidget();
+ Printer printer = new Printer();
+ StyledTextPrintOptions options = new StyledTextPrintOptions();
+
+ options.printTextForeground = true;
+ options.printTextBackground = true;
+ options.printTextFontStyle = true;
+ options.printLineBackground = true;
+ new Printing(this, printer, options).run();
+ printer.dispose();
+}
+/**
+ * Returns a runnable that will print the widget's text
+ * to the specified printer.
+ * <p>
+ * The runnable may be run in a non-UI thread.
+ * </p>
+ *
+ * @param printer the printer to print to
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <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 printer is null</li>
+ * </ul>
+ */
+public Runnable print(Printer printer) {
+ StyledTextPrintOptions options = new StyledTextPrintOptions();
+
+ checkWidget();
+ options.printTextForeground = true;
+ options.printTextBackground = true;
+ options.printTextFontStyle = true;
+ options.printLineBackground = true;
+ if (printer == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ return print(printer, options);
+}
+/**
+ * Returns a runnable that will print the widget's text
+ * to the specified printer.
+ * <p>
+ * The runnable may be run in a non-UI thread.
+ * </p>
+ *
+ * @param printer the printer to print to
+ * @param options print options to use during printing
+ * @exception SWTException <ul>
+ * <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 printer or options is null</li>
+ * </ul>
+ * @since 2.1
+ */
+public Runnable print(Printer printer, StyledTextPrintOptions options) {
+ checkWidget();
+ if (printer == null || options == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ return new Printing(this, printer, options);
+}
+/**
+ * Causes the entire bounds of the receiver to be marked
+ * as needing to be redrawn. The next time a paint request
+ * 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
+ * and that the content width may have changed.
+ * </p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Control#update
+ */
+public void redraw() {
+ int itemCount;
+
+ super.redraw();
+ itemCount = getPartialBottomIndex() - topIndex + 1;
+ lineCache.redrawReset(topIndex, itemCount, true);
+ lineCache.calculate(topIndex, itemCount);
+ setHorizontalScrollBar();
+}
+/**
+ * Causes the rectangular area of the receiver specified by
+ * the arguments to be marked as needing to be redrawn.
+ * The 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
+ * <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
+ * and that the content width may have changed.
+ * </p>
+ *
+ * @param x the x coordinate of the area to draw
+ * @param y the y coordinate of the area to draw
+ * @param width the width of the area to draw
+ * @param height the height of the area to draw
+ * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Control#update
+ */
+public void redraw(int x, int y, int width, int height, boolean all) {
+ super.redraw(x, y, width, height, all);
+ if (height > 0) {
+ int lineCount = content.getLineCount();
+ int startLine = (getTopPixel() + y) / lineHeight;
+ int endLine = startLine + Compatibility.ceil(height, lineHeight);
+ int itemCount;
+
+ // reset all lines in the redraw rectangle
+ startLine = Math.min(startLine, lineCount);
+ itemCount = Math.min(endLine, lineCount) - startLine;
+ lineCache.reset(startLine, itemCount, true);
+ // only calculate the visible lines
+ itemCount = getPartialBottomIndex() - topIndex + 1;
+ lineCache.calculate(topIndex, itemCount);
+ setHorizontalScrollBar();
+ }
+}
+/**
+ * Redraws a text range in the specified lines
+ * <p>
+ *
+ * @param firstLine first line to redraw at the specified offset
+ * @param offsetInFirstLine offset in firstLine to start redrawing
+ * @param lastLine last line to redraw
+ * @param endOffset offset in the last where redrawing should stop
+ * @param clearBackground true=clear the background by invalidating
+ * the requested redraw range, false=draw the foreground directly
+ * without invalidating the redraw range.
+ */
+void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
+ int lineCount = lastLine - firstLine + 1;
+ int redrawY = firstLine * lineHeight - verticalScrollOffset;
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ String line = content.getLine(firstLine);
+ GC gc = getGC();
+ StyledTextBidi bidi = getStyledTextBidi(line, firstLineOffset, gc);
+
+ bidi.redrawRange(
+ this, offsetInFirstLine,
+ Math.min(line.length(), endOffset) - offsetInFirstLine,
+ leftMargin - horizontalScrollOffset, redrawY + topMargin, lineHeight);
+ // redraw line break marker (either space or full client area width)
+ // if redraw range extends over more than one line and background should be redrawn
+ if (lastLine > firstLine && clearBackground) {
+ int lineBreakWidth;
+ int lineBreakStartX = bidi.getTextWidth();
+ // handle empty line case
+ if (lineBreakStartX == leftMargin) {
+ lineBreakStartX += XINSET;
+ }
+ lineBreakStartX = lineBreakStartX - horizontalScrollOffset;
+ if ((getStyle() & SWT.FULL_SELECTION) != 0) {
+ lineBreakWidth = getClientArea().width - lineBreakStartX;
+ }
+ else {
+ lineBreakWidth = lineEndSpaceWidth;
+ }
+ draw(lineBreakStartX, redrawY, lineBreakWidth, lineHeight, clearBackground);
+ }
+ // redraw last line if more than one line needs redrawing
+ if (lineCount > 1) {
+ int lastLineOffset = content.getOffsetAtLine(lastLine);
+ int offsetInLastLine = endOffset - lastLineOffset;
+ // no redraw necessary if redraw offset is 0
+ if (offsetInLastLine > 0) {
+ line = content.getLine(lastLine);
+ redrawY = lastLine * lineHeight - verticalScrollOffset;
+ bidi = getStyledTextBidi(line, lastLineOffset, gc);
+ bidi.redrawRange(
+ this, 0, offsetInLastLine,
+ leftMargin - horizontalScrollOffset,
+ redrawY + topMargin, lineHeight);
+ }
+ }
+ gc.dispose();
+}
+/**
+ * Redraw the given line.
+ * <p>
+ *
+ * @param line index of the line to redraw
+ * @param offset offset in line to start redrawing
+ */
+void redrawLine(int line, int offset) {
+ int redrawX = 0;
+ if (offset > 0) {
+ String lineText = content.getLine(line);
+ redrawX = getXAtOffset(lineText, line, offset);
+ }
+ int redrawY = line * lineHeight - verticalScrollOffset;
+ super.redraw(
+ redrawX + leftMargin, redrawY + topMargin,
+ getClientArea().width, lineHeight, true);
+}
+/**
+ * Redraws a text range in the specified lines
+ * <p>
+ *
+ * @param firstLine first line to redraw at the specified offset
+ * @param offsetInFirstLine offset in firstLine to start redrawing
+ * @param lastLine last line to redraw
+ * @param endOffset offset in the last where redrawing should stop
+ * @param clearBackground true=clear the background by invalidating
+ * the requested redraw range. If the redraw range includes the
+ * last character of a line (i.e., the entire line is redrawn) the
+ * line is cleared all the way to the right border of the widget.
+ * false=draw the foreground directly without invalidating the
+ * redraw range.
+ */
+void redrawLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
+ String line = content.getLine(firstLine);
+ int lineCount = lastLine - firstLine + 1;
+ int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine) - leftMargin;
+ int redrawStopX;
+ int redrawY = firstLine * lineHeight - verticalScrollOffset;
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ boolean fullLineRedraw = ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine);
+
+ // if redraw range includes last character on the first line,
+ // clear background to right widget border. fixes bug 19595.
+ if (clearBackground && endOffset - firstLineOffset >= line.length()) {
+ fullLineRedraw = true;
+ }
+ // calculate redraw stop location
+ if (fullLineRedraw) {
+ redrawStopX = getClientArea().width - leftMargin;
+ }
+ else {
+ redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset) - leftMargin;
+ }
+ draw(redrawX, redrawY, redrawStopX - redrawX, lineHeight, clearBackground);
+ // redraw last line if more than one line needs redrawing
+ if (lineCount > 1) {
+ int offsetInLastLine = endOffset - content.getOffsetAtLine(lastLine);
+ // no redraw necessary if redraw offset is 0
+ if (offsetInLastLine > 0) {
+ line = content.getLine(lastLine);
+ // if redraw range includes last character on the last line,
+ // clear background to right widget border. fixes bug 19595.
+ if (clearBackground && offsetInLastLine >= line.length()) {
+ fullLineRedraw = true;
+ }
+ if (fullLineRedraw) {
+ redrawStopX = getClientArea().width - leftMargin;
+ }
+ else {
+ redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine) - leftMargin;
+ }
+ redrawY = lastLine * lineHeight - verticalScrollOffset;
+ draw(0, redrawY, redrawStopX, lineHeight, clearBackground);
+ }
+ }
+}
+/**
+ * Fixes the widget to display a text change.
+ * Bit blitting and redrawing is done as necessary.
+ * <p>
+ *
+ * @param y y location of the text change
+ * @param newLineCount number of new lines.
+ * @param replacedLineCount number of replaced lines.
+ */
+void redrawMultiLineChange(int y, int newLineCount, int replacedLineCount) {
+ Rectangle clientArea = getClientArea();
+ int lineCount = newLineCount - replacedLineCount;
+ int sourceY;
+ int destinationY;
+
+ if (lineCount > 0) {
+ sourceY = Math.max(0, y + lineHeight);
+ destinationY = sourceY + lineCount * lineHeight;
+ }
+ else {
+ destinationY = Math.max(0, y + lineHeight);
+ sourceY = destinationY - lineCount * lineHeight;
+ }
+ scroll(
+ 0, destinationY, // destination x, y
+ 0, sourceY, // source x, y
+ clientArea.width, clientArea.height, true);
+ // Always redrawing causes the bottom line to flash when a line is
+ // deleted. This is because SWT merges the paint area of the scroll
+ // with the paint area of the redraw call below.
+ // To prevent this we could call update after the scroll. However,
+ // adding update can cause even more flash if the client does other
+ // redraw/update calls (ie. for syntax highlighting).
+ // We could also redraw only when a line has been added or when
+ // contents has been added to a line. This would require getting
+ // line index info from the content and is not worth the trouble
+ // (the flash is only on the bottom line and minor).
+ // Specifying the NO_MERGE_PAINTS style bit prevents the merged
+ // redraw but could cause flash/slowness elsewhere.
+ if (y + lineHeight > 0 && y <= clientArea.height) {
+ // redraw first changed line in case a line was split/joined
+ super.redraw(0, y, clientArea.width, lineHeight, true);
+ }
+ if (newLineCount > 0) {
+ int redrawStartY = y + lineHeight;
+ int redrawHeight = newLineCount * lineHeight;
+
+ if (redrawStartY + redrawHeight > 0 && redrawStartY <= clientArea.height) {
+ // display new text
+ super.redraw(0, redrawStartY, clientArea.width, redrawHeight, true);
+ }
+ }
+}
+/**
+ * Redraws the specified text range.
+ * <p>
+ *
+ * @param start offset of the first character to redraw
+ * @param length number of characters to redraw
+ * @param clearBackground true if the background should be cleared as
+ * part of the redraw operation. If true, the entire redraw range will
+ * be cleared before anything is redrawn. If the redraw range includes
+ * the last character of a line (i.e., the entire line is redrawn) the
+ * line is cleared all the way to the right border of the widget.
+ * The redraw operation will be faster and smoother if clearBackground
+ * is set to false. Whether or not the flag can be set to false depends
+ * on the type of change that has taken place. If font styles or
+ * background colors for the redraw range have changed, clearBackground
+ * should be set to true. If only foreground colors have changed for
+ * the redraw range, clearBackground can be set to false.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when start and/or end are outside the widget content</li>
+ * </ul>
+ */
+public void redrawRange(int start, int length, boolean clearBackground) {
+ checkWidget();
+ int end = start + length;
+ int contentLength = content.getCharCount();
+ int firstLine;
+ int lastLine;
+
+ if (start > end || start < 0 || end > contentLength) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ firstLine = content.getLineAtOffset(start);
+ lastLine = content.getLineAtOffset(end);
+ // reset all affected lines but let the redraw recalculate only
+ // those that are visible.
+ lineCache.reset(firstLine, lastLine - firstLine + 1, true);
+ internalRedrawRange(start, length, clearBackground);
+}
+/**
+ * Removes the specified bidirectional segment listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ * @since 2.0
+ */
+public void removeBidiSegmentListener(BidiSegmentListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ removeListener(LineGetSegments, listener);
+}
+/**
+ * Removes the specified extended modify listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeExtendedModifyListener(ExtendedModifyListener extendedModifyListener) {
+ checkWidget();
+ if (extendedModifyListener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ removeListener(ExtendedModify, extendedModifyListener);
+}
+/**
+ * Removes the specified line background listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeLineBackgroundListener(LineBackgroundListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ removeListener(LineGetBackground, listener);
+ // use default line styler if last user line styler was removed.
+ if (isListening(LineGetBackground) == false && userLineBackground) {
+ StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
+ addListener(LineGetBackground, typedListener);
+ userLineBackground = false;
+ }
+}
+/**
+ * Removes the specified line style listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeLineStyleListener(LineStyleListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ removeListener(LineGetStyle, listener);
+ // use default line styler if last user line styler was removed. Fixes 1G7B1X2
+ if (isListening(LineGetStyle) == false && userLineStyle) {
+ StyledTextListener typedListener = new StyledTextListener(defaultLineStyler);
+ addListener(LineGetStyle, typedListener);
+ userLineStyle = false;
+ }
+}
+/**
+ * Removes the specified modify listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeModifyListener(ModifyListener modifyListener) {
+ checkWidget();
+ if (modifyListener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ removeListener(SWT.Modify, modifyListener);
+}
+/**
+ * Removes the specified selection listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ removeListener(SWT.Selection, listener);
+}
+/**
+ * Removes the specified verify listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeVerifyListener(VerifyListener verifyListener) {
+ checkWidget();
+ if (verifyListener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ removeListener(SWT.Verify, verifyListener);
+}
+/**
+ * Removes the specified key verify listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeVerifyKeyListener(VerifyKeyListener listener) {
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ removeListener(VerifyKey, listener);
+}
+/**
+ * Replaces the styles in the given range with new styles. This method
+ * effectively deletes the styles in the given range and then adds the
+ * the new styles.
+ * <p>
+ * 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
+ * and length. The style rendering is undefined if the ranges do overlap
+ * or are ill-defined. Must not be null.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
+ * <li>ERROR_NULL_ARGUMENT when string is null</li>
+ * </ul>
+ * @since 2.0
+ */
+public void replaceStyleRanges(int start, int length, StyleRange[] ranges) {
+ checkWidget();
+ if (userLineStyle) {
+ return;
+ }
+ if (ranges == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ if (ranges.length == 0) {
+ setStyleRange(new StyleRange(start, length, null, null));
+ return;
+ }
+ int end = start + length;
+ if (start > end || start < 0 || end > getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+
+ int firstLine = content.getLineAtOffset(start);
+ int lastLine = content.getLineAtOffset(end);
+
+ // if the area is not visible, there is no need to redraw
+ boolean redrawLines = isAreaVisible(firstLine, lastLine);
+
+ if (!redrawLines) {
+ defaultLineStyler.replaceStyleRanges(start, length, ranges);
+ lineCache.reset(firstLine, lastLine - firstLine + 1, true);
+ } else {
+ boolean redrawFirstLine = false;
+ boolean redrawLastLine = false;
+ // the first and last line needs to be redrawn completely if the
+ // font style is changing from SWT.NORMAL to something else or
+ // vice versa. fixes 1G7M5WE.
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ if (isBidi()) {
+ redrawFirstLine = true;
+ redrawLastLine = true;
+ } else {
+ int firstLineEnd = firstLineOffset + content.getLine(firstLine).length();
+ redrawFirstLine = isRedrawFirstLine(ranges, firstLine, firstLineOffset);
+ // check if any bold styles will be cleared
+ StyleRange clearRange = new StyleRange(firstLineOffset, firstLineEnd - firstLineOffset, null, null);
+ redrawFirstLine = redrawFirstLine || isRedrawFirstLine(new StyleRange[] {clearRange}, firstLine, firstLineOffset);
+ if (lastLine != firstLine) {
+ int lastLineOffset = content.getOffsetAtLine(lastLine);
+ int lastLineEnd = lastLineOffset + content.getLine(lastLine).length();
+ redrawLastLine = isRedrawLastLine(ranges, lastLine, lastLineOffset);
+ // check if any bold styles will be cleared
+ clearRange = new StyleRange(lastLineOffset, lastLineEnd - lastLineOffset, null, null);
+ redrawLastLine = redrawLastLine || isRedrawLastLine(new StyleRange[] {clearRange}, lastLine, lastLineOffset);
+ }
+ }
+ defaultLineStyler.replaceStyleRanges(start, length, ranges);
+ // reset all lines affected by the style change but let the redraw
+ // recalculate only those that are visible.
+ lineCache.reset(firstLine, lastLine - firstLine + 1, true);
+ internalRedrawRange(start, length, true);
+ if (redrawFirstLine) {
+ redrawLine(firstLine, start - firstLineOffset);
+ }
+ if (redrawLastLine) {
+ redrawLine(lastLine, 0);
+ }
+ }
+
+ // make sure that the caret is positioned correctly.
+ // caret location may change if font style changes.
+ // fixes 1G8FODP
+ setCaretLocation();
+}
+/**
+ * Replaces the given text range with new text.
+ * If the widget has the SWT.SINGLE style and "text" contains more than
+ * one line, only the first line is rendered but the text is stored
+ * unchanged. A subsequent call to getText will return the same text
+ * that was set. Note that only a single line of text should be set when
+ * the SWT.SINGLE style is used.
+ * <p>
+ * <b>NOTE:</b> During the replace operation the current selection is
+ * changed as follows:
+ * <ul>
+ * <li>selection before replaced text: selection unchanged
+ * <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
+ * </ul>
+ * </p>
+ *
+ * @param start offset of first character to replace
+ * @param length number of characters to replace. Use 0 to insert text
+ * @param text new text. May be empty to delete text.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when either start or end is outside the valid range (0 <= offset <= getCharCount())</li>
+ * <li>ERROR_INVALID_ARGUMENT when either start or end is inside a multi byte line delimiter.
+ * Splitting a line delimiter for example by inserting text in between the CR and LF and deleting part of a line delimiter is not supported</li>
+ * <li>ERROR_NULL_ARGUMENT when string is null</li>
+ * </ul>
+ */
+public void replaceTextRange(int start, int length, String text) {
+ checkWidget();
+ int contentLength = getCharCount();
+ int end = start + length;
+ Event event = new Event();
+
+ if (start > end || start < 0 || end > contentLength) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ if (text == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ event.start = start;
+ event.end = end;
+ event.text = text;
+ modifyContent(event, false);
+}
+/**
+ * Resets the caret position, selection and scroll offsets. Recalculate
+ * the content width and scroll bars. Redraw the widget.
+ */
+void reset() {
+ ScrollBar verticalBar = getVerticalBar();
+ ScrollBar horizontalBar = getHorizontalBar();
+ caretOffset = 0;
+ topIndex = 0;
+ topOffset = 0;
+ verticalScrollOffset = 0;
+ horizontalScrollOffset = 0;
+ resetSelection();
+ // discard any styles that may have been set by creating a
+ // new default line styler
+ if (defaultLineStyler != null) {
+ removeLineBackgroundListener(defaultLineStyler);
+ removeLineStyleListener(defaultLineStyler);
+ installDefaultLineStyler();
+ }
+ calculateContentWidth();
+ if (verticalBar != null) {
+ verticalBar.setSelection(0);
+ }
+ if (horizontalBar != null) {
+ horizontalBar.setSelection(0);
+ }
+ setScrollBars();
+ setCaretLocation();
+ super.redraw();
+}
+/**
+ * Resets the selection.
+ */
+void resetSelection() {
+ selection.x = selection.y = caretOffset;
+ selectionAnchor = -1;
+}
+/**
+ * Scrolls the widget horizontally.
+ * <p>
+ *
+ * @param pixels number of pixels to scroll, > 0 = scroll left,
+ * < 0 scroll right
+ */
+void scrollHorizontal(int pixels) {
+ Rectangle clientArea;
+
+ if (pixels == 0) {
+ return;
+ }
+ clientArea = getClientArea();
+ if (pixels > 0) {
+ int sourceX = leftMargin + pixels;
+ int scrollWidth = clientArea.width - sourceX - rightMargin;
+ int scrollHeight = clientArea.height - topMargin - bottomMargin;
+ scroll(
+ leftMargin, topMargin, // destination x, y
+ sourceX, topMargin, // source x, y
+ scrollWidth, scrollHeight, true);
+ if (sourceX > scrollWidth) {
+ // redraw from end of scrolled area to beginning of scroll
+ // invalidated area
+ super.redraw(
+ leftMargin + scrollWidth, topMargin,
+ pixels - scrollWidth, scrollHeight, true);
+ }
+ }
+ else {
+ int destinationX = leftMargin - pixels;
+ int scrollWidth = clientArea.width - destinationX - rightMargin;
+ int scrollHeight = clientArea.height - topMargin - bottomMargin;
+ scroll(
+ destinationX, topMargin, // destination x, y
+ leftMargin, topMargin, // source x, y
+ scrollWidth, scrollHeight, true);
+ if (destinationX > scrollWidth) {
+ // redraw from end of scroll invalidated area to scroll
+ // destination
+ super.redraw(
+ leftMargin + scrollWidth, topMargin,
+ -pixels - scrollWidth, scrollHeight, true);
+ }
+ }
+ horizontalScrollOffset += pixels;
+ setCaretLocation();
+}
+/**
+ * Scrolls the widget horizontally and adjust the horizontal scroll
+ * bar to reflect the new horizontal offset..
+ * <p>
+ *
+ * @param pixels number of pixels to scroll, > 0 = scroll left,
+ * < 0 scroll right
+ * @return
+ * true=the widget was scrolled
+ * false=the widget was not scrolled, the given offset is not valid.
+ */
+boolean scrollHorizontalBar(int pixels) {
+ if (pixels == 0) {
+ return false;
+ }
+ ScrollBar horizontalBar = getHorizontalBar();
+ if (horizontalBar != null) {
+ horizontalBar.setSelection(horizontalScrollOffset + pixels);
+ }
+ scrollHorizontal(pixels);
+ return true;
+}
+/**
+ * Selects all the text.
+ * <p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll() {
+ checkWidget();
+ setSelection(new Point(0, Math.max(getCharCount(),0)));
+}
+/**
+ * Replaces/inserts text as defined by the event.
+ * <p>
+ *
+ * @param event the text change event.
+ * <ul>
+ * <li>event.start - the replace start offset</li>
+ * <li>event.end - the replace end offset</li>
+ * <li>event.text - the new text</li>
+ * </ul>
+ */
+void sendKeyEvent(Event event) {
+ if (editable == false) {
+ return;
+ }
+ modifyContent(event, true);
+}
+/**
+ * Sends the specified selection event.
+ */
+void sendSelectionEvent() {
+ Event event = new Event();
+ event.x = selection.x;
+ event.y = selection.y;
+ notifyListeners(SWT.Selection, event);
+}
+/**
+ * Sets whether the widget wraps lines.
+ * This overrides the creation style bit SWT.WRAP.
+ * <p>
+ *
+ * @param wrap true=widget wraps lines, false=widget does not wrap lines
+ * @since 2.0
+ */
+public void setWordWrap(boolean wrap) {
+ checkWidget();
+
+ if (wrap != wordWrap) {
+ ScrollBar horizontalBar = getHorizontalBar();
+
+ wordWrap = wrap;
+ if (wordWrap) {
+ logicalContent = content;
+ content = new WrappedContent(renderer, logicalContent);
+ }
+ else {
+ content = logicalContent;
+ }
+ calculateContentWidth();
+ horizontalScrollOffset = 0;
+ if (horizontalBar != null) {
+ horizontalBar.setVisible(!wordWrap);
+ }
+ setScrollBars();
+ setCaretLocation();
+ super.redraw();
+ }
+}
+/**
+ * Sets the caret location and scrolls the caret offset into view.
+ */
+void showBidiCaret() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+ String lineText = content.getLine(line);
+ int xAtOffset = 0;
+ boolean scrolled = false;
+ GC gc = getGC();
+ StyledTextBidi bidi = getStyledTextBidi(lineText, lineOffset, gc);
+ // getXAtOffset, inlined for better performance
+ xAtOffset = getBidiTextPosition(lineText, offsetInLine, bidi) + leftMargin;
+ if (offsetInLine > lineText.length()) {
+ // offset is not on the line. return an x location one character
+ // after the line to indicate the line delimiter.
+ xAtOffset += lineEndSpaceWidth;
+ }
+ xAtOffset -= horizontalScrollOffset;
+ //
+ scrolled = showLocation(xAtOffset, line);
+ if (scrolled == false) {
+ setBidiCaretLocation(bidi);
+ }
+ gc.dispose();
+}
+/**
+ * Sets the receiver's caret. Set the caret's height and location.
+ *
+ * </p>
+ * @param caret the new caret for the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setCaret(Caret caret) {
+ checkWidget ();
+ super.setCaret(caret);
+ if (caret != null) {
+ if (isBidi() == false) {
+ caret.setSize(caret.getSize().x, lineHeight);
+ }
+ setCaretLocation();
+ if (isBidi()) {
+ setBidiKeyboardLanguage();
+ }
+ }
+}
+/**
+ * @see org.eclipse.swt.widgets.Control#setBackground
+ */
+public void setBackground(Color color) {
+ checkWidget();
+ super.setBackground(color);
+ background = color;
+ redraw();
+}
+/**
+ * Set the caret to indicate the current typing direction.
+ */
+void setBidiCaretDirection() {
+ Caret caret = getCaret();
+ int direction = StyledTextBidi.getKeyboardLanguageDirection();
+
+ if (caret == null || direction == caretDirection) {
+ return;
+ }
+ caretDirection = direction;
+ if (direction == SWT.DEFAULT) {
+ caret.setImage(null);
+ caret.setSize(caret.getSize().x, lineHeight);
+ }
+ else
+ if (caretDirection == SWT.LEFT) {
+ caret.setImage(leftCaretBitmap);
+ }
+ else
+ if (caretDirection == SWT.RIGHT) {
+ caret.setImage(rightCaretBitmap);
+ }
+}
+/**
+ * Moves the Caret to the current caret offset.
+ * <p>
+ *
+ * @param bidi StyledTextBidi object to use for measuring.
+ * May be left null in which case a new object will be created.
+ */
+void setBidiCaretLocation(StyledTextBidi bidi) {
+ int caretLine = getCaretLine();
+
+ setBidiCaretLocation(bidi, caretLine);
+}
+/**
+ * Moves the Caret to the current caret offset.
+ * <p>
+ *
+ * @param bidi StyledTextBidi object to use for measuring.
+ * May be left null in which case a new object will be created.
+ * @param caretLine line the caret should be placed on. Relative to
+ * first line in document
+ */
+void setBidiCaretLocation(StyledTextBidi bidi, int caretLine) {
+ Caret caret = getCaret();
+ String lineText = content.getLine(caretLine);
+ int lineStartOffset = content.getOffsetAtLine(caretLine);
+ int offsetInLine = caretOffset - lineStartOffset;
+ GC gc = null;
+
+ if (bidi == null) {
+ gc = getGC();
+ bidi = getStyledTextBidi(lineText, lineStartOffset, gc);
+ }
+ if (lastCaretDirection == SWT.NULL) {
+ columnX = bidi.getTextPosition(offsetInLine) + leftMargin - horizontalScrollOffset;
+ } else {
+ columnX = bidi.getTextPosition(offsetInLine, lastCaretDirection) + leftMargin - horizontalScrollOffset;
+ }
+ if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
+ columnX -= (getCaretWidth() - 1);
+ }
+ if (caret != null) {
+ setBidiCaretDirection();
+ caret.setLocation(
+ columnX,
+ caretLine * lineHeight - verticalScrollOffset + topMargin);
+ }
+ if (gc != null) {
+ gc.dispose();
+ }
+}
+/**
+ * Sets the BIDI coloring mode. When true the BIDI text display
+ * algorithm is applied to segments of text that are the same
+ * color.
+ *
+ * @param mode the new coloring mode
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * <p>
+ * @deprecated use BidiSegmentListener instead.
+ * </p>
+ */
+public void setBidiColoring(boolean mode) {
+ checkWidget();
+ bidiColoring = mode;
+}
+/**
+ * Switches the keyboard language according to the current editing
+ * position and cursor direction.
+ */
+void setBidiKeyboardLanguage() {
+ int caretLine = getCaretLine();
+ int lineStartOffset = content.getOffsetAtLine(caretLine);
+ int offsetInLine = caretOffset - lineStartOffset;
+ String lineText = content.getLine(caretLine);
+ GC gc = getGC();
+ StyledTextBidi bidi;
+ int lineLength = lineText.length();
+
+ // Don't supply the bold styles/font since we don't want to measure anything
+ bidi = new StyledTextBidi(gc, lineText, getBidiSegments(lineStartOffset, lineText));
+ if (offsetInLine == 0) {
+ bidi.setKeyboardLanguage(offsetInLine);
+ }
+ else
+ if (offsetInLine >= lineLength) {
+ offsetInLine = Math.min(offsetInLine, lineLength - 1);
+ bidi.setKeyboardLanguage(offsetInLine);
+ }
+ else
+ if (lastCaretDirection == ST.COLUMN_NEXT) {
+ // continue with previous character type
+ bidi.setKeyboardLanguage(offsetInLine - 1);
+ }
+ else {
+ bidi.setKeyboardLanguage(offsetInLine);
+ }
+ gc.dispose();
+}
+/**
+ * Moves the Caret to the current caret offset.
+ * <p>
+ *
+ * @param newCaretX the new x location of the caret.
+ * passed in for better performance when it has already been
+ * calculated outside this method.
+ * @param line index of the line the caret is on. Relative to
+ * the first line in the document.
+ */
+void setCaretLocation(int newCaretX, int line) {
+ if (isBidi()) {
+ setBidiCaretLocation(null, line);
+ }
+ else {
+ Caret caret = getCaret();
+
+ columnX = newCaretX;
+ if (caret != null) {
+ caret.setLocation(
+ newCaretX,
+ line * lineHeight - verticalScrollOffset + topMargin);
+ }
+ }
+}
+/**
+ * Moves the Caret to the current caret offset.
+ */
+void setCaretLocation() {
+ if (isBidi()) {
+ setBidiCaretLocation(null);
+ }
+ else {
+ Caret caret = getCaret();
+ int caretLine = getCaretLine();
+ int lineStartOffset = content.getOffsetAtLine(caretLine);
+
+ columnX = getXAtOffset(
+ content.getLine(caretLine), caretLine, caretOffset - lineStartOffset);
+ if (caret != null) {
+ caret.setLocation(
+ columnX,
+ caretLine * lineHeight - verticalScrollOffset + topMargin);
+ }
+ }
+}
+/**
+ * Sets the caret offset.
+ *
+ * @param offset caret offset, relative to the first character in the text.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
+ * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
+ * </ul>
+ */
+public void setCaretOffset(int offset) {
+ checkWidget();
+ int length = getCharCount();
+
+ if (length > 0 && offset != caretOffset) {
+ if (offset < 0) {
+ caretOffset = 0;
+ }
+ else
+ if (offset > length) {
+ caretOffset = length;
+ }
+ else {
+ if (isLineDelimiter(offset)) {
+ // offset is inside a multi byte line delimiter. This is an
+ // illegal operation and an exception is thrown. Fixes 1GDKK3R
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ caretOffset = offset;
+ }
+ // clear the selection if the caret is moved.
+ // don't notify listeners about the selection change.
+ clearSelection(false);
+ }
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+ if (isBidi()) {
+ setBidiKeyboardLanguage();
+ }
+}
+/**
+ * Copies the specified text range to the clipboard. The text will be placed
+ * in the clipboard in plain text format and RTF format.
+ * <p>
+ *
+ * @param start start index of the text
+ * @param length length of text to place in clipboard
+ *
+ * @exception SWTError, see Clipboard.setContents
+ * @see org.eclipse.swt.dnd.Clipboard.setContents
+ */
+void setClipboardContent(int start, int length) throws SWTError {
+ RTFTransfer rtfTransfer = RTFTransfer.getInstance();
+ TextTransfer plainTextTransfer = TextTransfer.getInstance();
+ RTFWriter rtfWriter = new RTFWriter(start, length);
+ TextWriter plainTextWriter = new TextWriter(start, length);
+ String rtfText = getPlatformDelimitedText(rtfWriter);
+ String plainText = getPlatformDelimitedText(plainTextWriter);
+
+ clipboard.setContents(
+ new String[]{rtfText, plainText},
+ new Transfer[]{rtfTransfer, plainTextTransfer});
+}
+/**
+ * Sets the content implementation to use for text storage.
+ * <p>
+ *
+ * @param content StyledTextContent implementation to use for text storage.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void setContent(StyledTextContent newContent) {
+ checkWidget();
+ if (newContent == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ if (content != null) {
+ content.removeTextChangeListener(textChangeListener);
+ }
+ logicalContent = newContent;
+ if (wordWrap) {
+ content = new WrappedContent(renderer, logicalContent);
+ }
+ else {
+ content = logicalContent;
+ }
+ content.addTextChangeListener(textChangeListener);
+ reset();
+}
+/**
+ * Sets the receiver's cursor to the cursor specified by the
+ * argument. Overridden to handle the null case since the
+ * StyledText widget uses an ibeam as its default cursor.
+ *
+ * @see org.eclipse.swt.widgets.Control#setCursor
+ */
+public void setCursor (Cursor cursor) {
+ if (cursor == null) {
+ super.setCursor(ibeamCursor);
+ } else {
+ super.setCursor(cursor);
+ }
+}
+/**
+ * Sets whether the widget implements double click mouse behavior.
+ * </p>
+ *
+ * @param enable if true double clicking a word selects the word, if false
+ * double clicks have the same effect as regular mouse clicks.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDoubleClickEnabled(boolean enable) {
+ checkWidget();
+ doubleClickEnabled = enable;
+}
+/**
+ * Sets whether the widget content can be edited.
+ * </p>
+ *
+ * @param editable if true content can be edited, if false content can not be
+ * edited
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEditable(boolean editable) {
+ checkWidget();
+ this.editable = editable;
+}
+/**
+ * Sets a new font to render text with.
+ * <p>
+ * <b>NOTE:</b> Italic fonts are not supported unless they have no overhang
+ * and the same baseline as regular fonts.
+ * </p>
+ *
+ * @param font new font
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setFont(Font font) {
+ checkWidget();
+ int oldLineHeight = lineHeight;
+
+ super.setFont(font);
+ initializeRenderer();
+ // keep the same top line visible. fixes 5815
+ if (lineHeight != oldLineHeight) {
+ setVerticalScrollOffset(verticalScrollOffset * lineHeight / oldLineHeight, true);
+ claimBottomFreeSpace();
+ }
+ calculateContentWidth();
+ calculateScrollBars();
+ if (isBidi()) {
+ caretDirection = SWT.NULL;
+ createCaretBitmaps();
+ setBidiCaretDirection();
+ }
+ else {
+ Caret caret = getCaret();
+ if (caret != null) {
+ caret.setSize(caret.getSize().x, lineHeight);
+ }
+ }
+ // always set the caret location. Fixes 6685
+ setCaretLocation();
+ super.redraw();
+}
+/**
+ * @see org.eclipse.swt.widgets.Control#setForeground
+ */
+public void setForeground(Color color) {
+ checkWidget();
+ super.setForeground(color);
+ foreground = color;
+ redraw();
+}
+/**
+ * Sets the horizontal scroll offset relative to the start of the line.
+ * Do nothing if there is no text set.
+ * <p>
+ * <b>NOTE:</b> The horizontal index is reset to 0 when new text is set in the
+ * widget.
+ * </p>
+ *
+ * @param offset horizontal scroll offset relative to the start
+ * of the line, measured in character increments starting at 0, if
+ * equal to 0 the content is not scrolled, if > 0 = the content is scrolled.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setHorizontalIndex(int offset) {
+ checkWidget();
+ int clientAreaWidth = getClientArea().width;
+ if (getCharCount() == 0) {
+ return;
+ }
+ if (offset < 0) {
+ offset = 0;
+ }
+ offset *= getHorizontalIncrement();
+ // allow any value if client area width is unknown or 0.
+ // offset will be checked in resize handler.
+ // don't use isVisible since width is known even if widget
+ // is temporarily invisible
+ if (clientAreaWidth > 0) {
+ int width = lineCache.getWidth();
+ // prevent scrolling if the content fits in the client area.
+ // align end of longest line with right border of client area
+ // if offset is out of range.
+ if (offset > width - clientAreaWidth) {
+ offset = Math.max(0, width - clientAreaWidth);
+ }
+ }
+ scrollHorizontalBar(offset - horizontalScrollOffset);
+}
+/**
+ * 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
+ * is set in the widget.
+ * </p>
+ *
+ * @param pixel horizontal pixel offset relative to the start
+ * of the line.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 2.0
+ */
+public void setHorizontalPixel(int pixel) {
+ checkWidget();
+ int clientAreaWidth = getClientArea().width;
+ if (getCharCount() == 0) {
+ return;
+ }
+ if (pixel < 0) {
+ pixel = 0;
+ }
+ // allow any value if client area width is unknown or 0.
+ // offset will be checked in resize handler.
+ // don't use isVisible since width is known even if widget
+ // is temporarily invisible
+ if (clientAreaWidth > 0) {
+ int width = lineCache.getWidth();
+ // prevent scrolling if the content fits in the client area.
+ // align end of longest line with right border of client area
+ // if offset is out of range.
+ if (pixel > width - clientAreaWidth) {
+ pixel = Math.max(0, width - clientAreaWidth);
+ }
+ }
+ scrollHorizontalBar(pixel - horizontalScrollOffset);
+}
+/**
+ * Adjusts the maximum and the page size of the horizontal scroll bar
+ * to reflect content width changes.
+ */
+void setHorizontalScrollBar() {
+ ScrollBar horizontalBar = getHorizontalBar();
+
+ if (horizontalBar != null && horizontalBar.getVisible()) {
+ final int INACTIVE = 1;
+ Rectangle clientArea = getClientArea();
+ // only set the real values if the scroll bar can be used
+ // (ie. because the thumb size is less than the scroll maximum)
+ // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
+ if (clientArea.width < lineCache.getWidth()) {
+ horizontalBar.setValues(
+ horizontalBar.getSelection(),
+ horizontalBar.getMinimum(),
+ lineCache.getWidth(), // maximum
+ clientArea.width - leftMargin - rightMargin, // thumb size
+ horizontalBar.getIncrement(),
+ clientArea.width - leftMargin - rightMargin); // page size
+ }
+ else
+ if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) {
+ horizontalBar.setValues(
+ horizontalBar.getSelection(),
+ horizontalBar.getMinimum(),
+ INACTIVE,
+ INACTIVE,
+ horizontalBar.getIncrement(),
+ INACTIVE);
+ }
+ }
+}
+/**
+ * Sets the background color of the specified lines.
+ * The background color is drawn for the width of the widget. All
+ * line background colors are discarded when setText is called.
+ * The text background color if defined in a StyleRange overlays the
+ * line background color. Should not be called if a LineBackgroundListener
+ * has been set since the listener maintains the line backgrounds.
+ * <p>
+ * Line background colors are maintained relative to the line text, not the
+ * line index that is specified in this method call.
+ * During text changes, when entire lines are inserted or removed, the line
+ * background colors that are associated with the lines after the change
+ * will "move" with their respective text. An entire line is defined as
+ * extending from the first character on a line to the last and including the
+ * line delimiter.
+ * </p>
+ * <p>
+ * When two lines are joined by deleting a line delimiter, the top line
+ * background takes precedence and the color of the bottom line is deleted.
+ * For all other text changes line background colors will remain unchanged.
+ * </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
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT when the specified line range is invalid</li>
+ * </ul>
+ */
+public void setLineBackground(int startLine, int lineCount, Color background) {
+ checkWidget();
+ int partialBottomIndex = getPartialBottomIndex();
+
+ // this API can not be used if the client is providing the line background
+ if (userLineBackground) {
+ return;
+ }
+ if (startLine < 0 || startLine + lineCount > logicalContent.getLineCount()) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ defaultLineStyler.setLineBackground(startLine, lineCount, background);
+ // do nothing if redraw range is completely invisible
+ if (startLine > partialBottomIndex || startLine + lineCount - 1 < topIndex) {
+ return;
+ }
+ // only redraw visible lines
+ if (startLine < topIndex) {
+ lineCount -= topIndex - startLine;
+ startLine = topIndex;
+ }
+ if (startLine + lineCount - 1 > partialBottomIndex) {
+ lineCount = partialBottomIndex - startLine + 1;
+ }
+ startLine -= topIndex;
+ super.redraw(
+ leftMargin, startLine * lineHeight + topMargin,
+ getClientArea().width - leftMargin - rightMargin, lineCount * lineHeight, true);
+}
+/**
+ * Flips selection anchor based on word selection direction.
+ */
+void setMouseWordSelectionAnchor() {
+ if (mouseDoubleClick == false) {
+ return;
+ }
+ if (caretOffset < doubleClickSelection.x) {
+ selectionAnchor = doubleClickSelection.y;
+ }
+ else
+ if (caretOffset > doubleClickSelection.y) {
+ selectionAnchor = doubleClickSelection.x;
+ }
+}
+/**
+ * Adjusts the maximum and the page size of the scroll bars to
+ * reflect content width/length changes.
+ */
+void setScrollBars() {
+ ScrollBar verticalBar = getVerticalBar();
+
+ if (verticalBar != null) {
+ Rectangle clientArea = getClientArea();
+ final int INACTIVE = 1;
+ int maximum = content.getLineCount() * getVerticalIncrement();
+
+ // only set the real values if the scroll bar can be used
+ // (ie. because the thumb size is less than the scroll maximum)
+ // avoids flashing on Motif, fixes 1G7RE1J and 1G5SE92
+ if (clientArea.height < maximum) {
+ verticalBar.setValues(
+ verticalBar.getSelection(),
+ verticalBar.getMinimum(),
+ maximum,
+ clientArea.height, // thumb size
+ verticalBar.getIncrement(),
+ clientArea.height); // page size
+ }
+ else
+ if (verticalBar.getThumb() != INACTIVE || verticalBar.getMaximum() != INACTIVE) {
+ verticalBar.setValues(
+ verticalBar.getSelection(),
+ verticalBar.getMinimum(),
+ INACTIVE,
+ INACTIVE,
+ verticalBar.getIncrement(),
+ INACTIVE);
+ }
+ }
+ setHorizontalScrollBar();
+}
+/**
+ * Sets the selection to the given position and scrolls it into view. Equivalent to setSelection(start,start).
+ * <p>
+ *
+ * @param start new caret position
+ * @see #setSelection(int,int)
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when start is outside the widget content
+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
+ * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
+ * </ul>
+ */
+public void setSelection(int start) {
+ // 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
+ * N+1 caret positions, ranging from 0..N
+ * </p>
+ *
+ * @param point x=selection start offset, y=selection end offset
+ * The caret will be placed at the selection start when x > y.
+ * @see #setSelection(int,int)
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when point is null</li>
+ * <li>ERROR_INVALID_RANGE when start or end is outside the widget content
+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
+ * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
+ * </ul>
+ */
+public void setSelection(Point point) {
+ checkWidget();
+ if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ setSelection(point.x, point.y);
+}
+/**
+ * Sets the receiver's selection background color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 2.1
+ */
+public void setSelectionBackground (Color color) {
+ checkWidget ();
+ if (color != null) {
+ if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ selectionBackground = color;
+ redraw();
+}
+/**
+ * Sets the receiver's selection foreground color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 2.1
+ */
+public void setSelectionForeground (Color color) {
+ checkWidget ();
+ if (color != null) {
+ if (color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ selectionForeground = color;
+ redraw();
+}
+/**
+ * Sets the selection and scrolls it into view.
+ * <p>
+ * Indexing is zero based. Text selections are specified in terms of
+ * caret positions. In a text widget that contains N characters, there are
+ * N+1 caret positions, ranging from 0..N
+ * </p>
+ *
+ * @param start selection start offset. The caret will be placed at the
+ * selection start when start > end.
+ * @param end selection end offset
+ * @see #setSelectionRange(int,int)
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when start or end is outside the widget content
+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
+ * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
+ * </ul>
+ */
+public void setSelection(int start, int end) {
+ // checkWidget test done in setSelectionRange
+ setSelectionRange(start, end - start);
+ showSelection();
+}
+/**
+ * Sets the selection. The new selection may not be visible. Call showSelection to scroll
+ * the selection into view.
+ * <p>
+ *
+ * @param start offset of the first selected character, start >= 0 must be true.
+ * @param length number of characters to select, 0 <= start + length <= getCharCount()
+ * must be true.
+ * A negative length places the caret at the visual start of the selection.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when the range specified by start and length is outside the widget content
+ * <li>ERROR_INVALID_ARGUMENT when either the start or the end of the selection range is inside a
+ * multi byte line delimiter (and thus neither clearly in front of or after the line delimiter)
+ * </ul>
+ */
+public void setSelectionRange(int start, int length) {
+ checkWidget();
+ int contentLength = getCharCount();
+ int end = start + length;
+
+ if (start < 0 || end < 0 || start > contentLength || end > contentLength) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ if (isLineDelimiter(start) || isLineDelimiter(end)) {
+ // the start offset or end offset of the selection range is inside a
+ // multi byte line delimiter. This is an illegal operation and an exception
+ // is thrown. Fixes 1GDKK3R
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ internalSetSelection(start, length, false);
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+ if (isBidi()) {
+ setBidiKeyboardLanguage();
+ }
+}
+/**
+ * Sets the selection.
+ * The new selection may not be visible. Call showSelection to scroll
+ * the selection into view.
+ * <p>
+ *
+ * @param start offset of the first selected character, start >= 0 must be true.
+ * @param length number of characters to select, 0 <= start + length
+ * <= getCharCount() must be true.
+ * A negative length places the caret at the selection start.
+ * @param sendEvent a Selection event is sent when set to true and when
+ * the selection is reset.
+ */
+void internalSetSelection(int start, int length, boolean sendEvent) {
+ int end = start + length;
+
+ if (start > end) {
+ int temp = end;
+ end = start;
+ start = temp;
+ }
+ // is the selection range different or is the selection direction
+ // different?
+ if (selection.x != start || selection.y != end ||
+ (length > 0 && selectionAnchor != selection.x) ||
+ (length < 0 && selectionAnchor != selection.y)) {
+ clearSelection(sendEvent);
+ if (length < 0) {
+ selectionAnchor = selection.y = end;
+ caretOffset = selection.x = start;
+ }
+ else {
+ selectionAnchor = selection.x = start;
+ caretOffset = selection.y = end;
+ }
+ internalRedrawRange(selection.x, selection.y - selection.x, true);
+ }
+}
+/**
+ * Adds the specified style. The new style overwrites existing styles for the
+ * specified range. Existing style ranges are adjusted if they partially
+ * overlap with the new style, To clear an individual style, call setStyleRange
+ * with a StyleRange that has null attributes.
+ * <p>
+ * Should not be called if a LineStyleListener has been set since the
+ * listener maintains the styles.
+ * </p>
+ *
+ * @param range StyleRange object containing the style information.
+ * Overwrites the old style in the given range. May be null to delete
+ * all styles.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE when the style range is outside the valid range (> getCharCount())</li>
+ * </ul>
+ */
+public void setStyleRange(StyleRange range) {
+ checkWidget();
+
+ // this API can not be used if the client is providing the line styles
+ if (userLineStyle) {
+ return;
+ }
+ // check the range, make sure it falls within the range of the
+ // text
+ if (range != null && range.start + range.length > content.getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ if (range != null) {
+ boolean redrawFirstLine = false;
+ boolean redrawLastLine = false;
+ int firstLine = content.getLineAtOffset(range.start);
+ int lastLine = content.getLineAtOffset(range.start + range.length);
+
+ // if the style is not visible, there is no need to redraw
+ boolean redrawLines = isAreaVisible(firstLine, lastLine);
+
+ if (!redrawLines) {
+ defaultLineStyler.setStyleRange(range);
+ lineCache.reset(firstLine, lastLine - firstLine + 1, true);
+ } else {
+ // the first and last line needs to be redrawn completely if the
+ // font style is changing from SWT.NORMAL to something else or
+ // vice versa. fixes 1G7M5WE.
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ int lastLineOffset = content.getOffsetAtLine(lastLine);
+ if (isBidi()) {
+ if (firstLine != lastLine) {
+ redrawFirstLine = true;
+ }
+ redrawLastLine = true;
+ } else {
+ redrawFirstLine = isRedrawFirstLine(new StyleRange[] {range}, firstLine, firstLineOffset);
+ if (lastLine != firstLine) {
+ redrawLastLine = isRedrawLastLine(new StyleRange[] {range}, lastLine, lastLineOffset);
+ }
+ }
+ defaultLineStyler.setStyleRange(range);
+ // reset all lines affected by the style change but let the redraw
+ // recalculate only those that are visible.
+ lineCache.reset(firstLine, lastLine - firstLine + 1, true);
+ internalRedrawRange(range.start, range.length, true);
+ if (redrawFirstLine) {
+ redrawLine(firstLine, range.start - firstLineOffset);
+ }
+ if (redrawLastLine) {
+ redrawLine(lastLine, 0);
+ }
+ }
+ } else {
+ // clearing all styles
+ defaultLineStyler.setStyleRange(range);
+ lineCache.reset(0, content.getLineCount(), false);
+ redraw();
+ }
+
+ // make sure that the caret is positioned correctly.
+ // caret location may change if font style changes.
+ // fixes 1G8FODP
+ setCaretLocation();
+}
+/**
+ * Sets styles to be used for rendering the widget content. All styles
+ * in the widget will be replaced with the given set of styles.
+ * <p>
+ * Should not be called if a LineStyleListener has been set since the
+ * 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 do overlap. Must not be null.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * <li>ERROR_INVALID_RANGE when the last of the style ranges is outside the valid range (> getCharCount())</li>
+ * </ul>
+ */
+public void setStyleRanges(StyleRange[] ranges) {
+ checkWidget();
+ // this API can not be used if the client is providing the line styles
+ if (userLineStyle) {
+ return;
+ }
+ if (ranges == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ // check the last range, make sure it falls within the range of the
+ // current text
+ if (ranges.length != 0) {
+ StyleRange last = ranges[ranges.length-1];
+ int lastEnd = last.start + last.length;
+ int firstLine = content.getLineAtOffset(ranges[0].start);
+ int lastLine;
+ if (lastEnd > content.getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ lastLine = content.getLineAtOffset(lastEnd);
+ // reset all lines affected by the style change
+ lineCache.reset(firstLine, lastLine - firstLine + 1, true);
+ }
+ else {
+ // reset all lines
+ lineCache.reset(0, content.getLineCount(), false);
+ }
+ defaultLineStyler.setStyleRanges(ranges);
+ redraw(); // should only redraw affected area to avoid flashing
+ // make sure that the caret is positioned correctly.
+ // caret location may change if font style changes.
+ // fixes 1G8FODP
+ setCaretLocation();
+}
+/**
+ * Sets the tab width.
+ * <p>
+ *
+ * @param tabs tab width measured in characters.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTabs(int tabs) {
+ checkWidget();
+ tabLength = tabs;
+ renderer.setTabLength(tabLength);
+ if (caretOffset > 0) {
+ caretOffset = 0;
+ if (isBidi()) {
+ showBidiCaret();
+ }
+ else {
+ showCaret();
+ }
+ clearSelection(false);
+ }
+ // reset all line widths when the tab width changes
+ lineCache.reset(0, content.getLineCount(), false);
+ redraw();
+}
+/**
+ * Sets the widget content.
+ * If the widget has the SWT.SINGLE style and "text" contains more than
+ * one line, only the first line is rendered but the text is stored
+ * unchanged. A subsequent call to getText will return the same text
+ * that was set.
+ * <p>
+ * <b>Note:</b> Only a single line of text should be set when the SWT.SINGLE
+ * style is used.
+ * </p>
+ *
+ * @param text new widget content. Replaces existing content. Line styles
+ * that were set using StyledText API are discarded. The
+ * current selection is also discarded.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when string is null</li>
+ * </ul>
+ */
+public void setText(String text) {
+ checkWidget();
+ Event event = new Event();
+
+ if (text == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ event.start = 0;
+ event.end = getCharCount();
+ event.text = text;
+ event.doit = true;
+ notifyListeners(SWT.Verify, event);
+ if (event.doit) {
+ StyledTextEvent styledTextEvent = null;
+
+ if (isListening(ExtendedModify)) {
+ styledTextEvent = new StyledTextEvent(logicalContent);
+ styledTextEvent.start = event.start;
+ styledTextEvent.end = event.start + event.text.length();
+ styledTextEvent.text = content.getTextRange(event.start, event.end - event.start);
+ }
+ content.setText(event.text);
+ notifyListeners(SWT.Modify, event);
+ if (styledTextEvent != null) {
+ notifyListeners(ExtendedModify, styledTextEvent);
+ }
+ }
+}
+/**
+ * Sets the text limit.
+ * <p>
+ * The text limit specifies the amount of text that
+ * the user can type into the widget.
+ * </p>
+ *
+ * @param limit the new text limit.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_CANNOT_BE_ZERO when limit is 0</li>
+ * </ul>
+ */
+public void setTextLimit(int limit) {
+ checkWidget();
+ if (limit == 0) {
+ SWT.error(SWT.ERROR_CANNOT_BE_ZERO);
+ }
+ textLimit = limit;
+}
+/**
+ * Sets the top index. Do nothing if there is no text set.
+ * <p>
+ * The top index is the index of the line that is currently at the top
+ * of the widget. The top index changes when the widget is scrolled.
+ * Indexing starts from zero.
+ * Note: The top index is reset to 0 when new text is set in the widget.
+ * </p>
+ *
+ * @param index new top index. Must be between 0 and
+ * getLineCount() - fully visible lines per page. If no lines are fully
+ * visible the maximum value is getLineCount() - 1. An out of range
+ * index will be adjusted accordingly.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTopIndex(int topIndex) {
+ checkWidget();
+ int lineCount = logicalContent.getLineCount();
+ int pageSize = Math.max(1, Math.min(lineCount, getLineCountWhole()));
+
+ if (getCharCount() == 0) {
+ return;
+ }
+ if (topIndex < 0) {
+ topIndex = 0;
+ }
+ else
+ if (topIndex > lineCount - pageSize) {
+ topIndex = lineCount - pageSize;
+ }
+ if (wordWrap) {
+ int logicalLineOffset = logicalContent.getOffsetAtLine(topIndex);
+ topIndex = content.getLineAtOffset(logicalLineOffset);
+ }
+ setVerticalScrollOffset(topIndex * getVerticalIncrement(), true);
+}
+/**
+ * Sets the top pixel offset. Do nothing if there is no text set.
+ * <p>
+ * The top pixel offset is the vertical pixel offset of the widget. The
+ * widget is scrolled so that the given pixel position is at the top.
+ * The top index is adjusted to the corresponding top line.
+ * Note: The top pixel is reset to 0 when new text is set in the widget.
+ * </p>
+ *
+ * @param pixel new top pixel offset. Must be between 0 and
+ * (getLineCount() - visible lines per page) / getLineHeight()). An out
+ * of range offset will be adjusted accordingly.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @since 2.0
+ */
+public void setTopPixel(int pixel) {
+ checkWidget();
+ int lineCount =content.getLineCount();
+ int height = getClientArea().height;
+ int maxTopPixel = Math.max(0, lineCount * getVerticalIncrement() - height);
+
+ if (getCharCount() == 0) {
+ return;
+ }
+ if (pixel < 0) {
+ pixel = 0;
+ }
+ else
+ if (pixel > maxTopPixel) {
+ pixel = maxTopPixel;
+ }
+ setVerticalScrollOffset(pixel, true);
+}
+/**
+ * Scrolls the widget vertically.
+ * <p>
+ *
+ * @param pixelOffset the new vertical scroll offset
+ * @param adjustScrollBar
+ * true= the scroll thumb will be moved to reflect the new scroll offset.
+ * false = the scroll thumb will not be moved
+ * @return
+ * true=the widget was scrolled
+ * false=the widget was not scrolled, the given offset is not valid.
+ */
+boolean setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) {
+ Rectangle clientArea;
+ ScrollBar verticalBar = getVerticalBar();
+
+ if (pixelOffset == verticalScrollOffset) {
+ return false;
+ }
+ if (verticalBar != null && adjustScrollBar) {
+ verticalBar.setSelection(pixelOffset);
+ }
+ clientArea = getClientArea();
+ scroll(
+ 0, 0, // destination x, y
+ 0, pixelOffset - verticalScrollOffset, // source x, y
+ clientArea.width, clientArea.height, true);
+
+ verticalScrollOffset = pixelOffset;
+ calculateTopIndex();
+ setCaretLocation();
+ return true;
+}
+/**
+ * Scrolls the specified location into view.
+ * <p>
+ *
+ * @param x the x coordinate that should be made visible.
+ * @param line the line that should be made visible. Relative to the
+ * first line in the document.
+ * @return
+ * true=the widget was scrolled to make the specified location visible.
+ * false=the specified location is already visible, the widget was
+ * not scrolled.
+ */
+boolean showLocation(int x, int line) {
+ int clientAreaWidth = getClientArea().width - leftMargin - rightMargin;
+ int verticalIncrement = getVerticalIncrement();
+ int horizontalIncrement = clientAreaWidth / 4;
+ boolean scrolled = false;
+
+ if (x < leftMargin) {
+ // always make 1/4 of a page visible
+ x = Math.max(horizontalScrollOffset * -1, x - horizontalIncrement);
+ scrolled = scrollHorizontalBar(x);
+ }
+ else
+ if (x >= clientAreaWidth) {
+ // always make 1/4 of a page visible
+ x = Math.min(lineCache.getWidth() - horizontalScrollOffset, x + horizontalIncrement);
+ scrolled = scrollHorizontalBar(x - clientAreaWidth);
+ }
+ if (line < topIndex) {
+ scrolled = setVerticalScrollOffset(line * verticalIncrement, true);
+ }
+ else
+ if (line > getBottomIndex()) {
+ scrolled = setVerticalScrollOffset((line + 1) * verticalIncrement - getClientArea().height, true);
+ }
+ return scrolled;
+}
+/**
+ * Sets the caret location and scrolls the caret offset into view.
+ */
+void showCaret() {
+ int caretLine = content.getLineAtOffset(caretOffset);
+
+ showCaret(caretLine);
+}
+/**
+ * Sets the caret location and scrolls the caret offset into view.
+ */
+void showCaret(int caretLine) {
+ int lineOffset = content.getOffsetAtLine(caretLine);
+ String lineText = content.getLine(caretLine);
+ int offsetInLine = caretOffset - lineOffset;
+ int xAtOffset = getXAtOffset(lineText, caretLine, offsetInLine);
+ boolean scrolled = showLocation(xAtOffset, caretLine);
+ boolean setWrapCaretLocation = false;
+ Caret caret = getCaret();
+
+ if (wordWrap && caret != null) {
+ int caretY = caret.getLocation().y;
+ if ((caretY + verticalScrollOffset) / getVerticalIncrement() - 1 != caretLine) {
+ setWrapCaretLocation = true;
+ }
+ }
+ if (scrolled == false || setWrapCaretLocation) {
+ // set the caret location if a scroll operation did not set it (as a
+ // sideeffect of scrolling) or when in word wrap mode and the caret
+ // line was explicitly specified (i.e., because getWrapCaretLine does
+ // not return the desired line causing scrolling to not set it correctly)
+ setCaretLocation(xAtOffset, caretLine);
+ }
+ if (isBidi()) {
+ setBidiKeyboardLanguage();
+ }
+}
+/**
+ * Scrolls the specified offset into view.
+ * <p>
+ *
+ * @param offset offset that should be scolled into view
+ */
+void showOffset(int offset) {
+ int line = content.getLineAtOffset(offset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = offset - lineOffset;
+ String lineText = content.getLine(line);
+ int xAtOffset = getXAtOffset(lineText, line, offsetInLine);
+
+ showLocation(xAtOffset, line);
+}
+/**
+/**
+ * Scrolls the selection into view. The end of the selection will be scrolled into
+ * view. Note that if a right-to-left selection exists, the end of the selection is the
+ * visual beginning of the selection (i.e., where the caret is located).
+ * <p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void showSelection() {
+ checkWidget();
+ boolean selectionFits;
+ int startOffset, startLine, startX, endOffset, endLine, endX, offsetInLine;
+
+ // is selection from right-to-left?
+ boolean rightToLeft = caretOffset == selection.x;
+
+ if (rightToLeft) {
+ startOffset = selection.y;
+ endOffset = selection.x;
+ } else {
+ startOffset = selection.x;
+ endOffset = selection.y;
+ }
+
+ // calculate the logical start and end values for the selection
+ startLine = content.getLineAtOffset(startOffset);
+ offsetInLine = startOffset - content.getOffsetAtLine(startLine);
+ startX = getXAtOffset(content.getLine(startLine), startLine, offsetInLine);
+ endLine = content.getLineAtOffset(endOffset);
+ offsetInLine = endOffset - content.getOffsetAtLine(endLine);
+ endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);
+
+ // can the selection be fully displayed within the widget's visible width?
+ int w = getClientArea().width;
+ if (rightToLeft) {
+ selectionFits = startX - endX <= w;
+ } else {
+ selectionFits = endX - startX <= w;
+ }
+
+ if (selectionFits) {
+ // show as much of the selection as possible by first showing
+ // the start of the selection
+ showLocation(startX, startLine);
+ // endX value could change if showing startX caused a scroll to occur
+ endX = getXAtOffset(content.getLine(endLine), endLine, offsetInLine);
+ showLocation(endX, endLine);
+ } else {
+ // just show the end of the selection since the selection start
+ // will not be visible
+ showLocation(endX, endLine);
+ }
+}
+/**
+ * Updates the caret direction when a delete operation occured based on
+ * the type of the delete operation (next/previous character) and the
+ * caret location (at a direction boundary or inside a direction segment).
+ * The intent is to place the caret at the visual location where a
+ * character was deleted.
+ * <p>
+ *
+ * @param isBackspace true=the previous character was deleted, false=the
+ * character next to the caret location was deleted
+ * @param isDirectionBoundary true=the caret is between a R2L and L2R segment,
+ * false=the caret is within a direction segment
+ */
+void updateBidiDirection(boolean isBackspace, boolean isDirectionBoundary) {
+ if (isDirectionBoundary) {
+ if (isBackspace) {
+ // Deleted previous character (backspace) at a direction boundary
+ // Go to direction segment of deleted character
+ lastCaretDirection = ST.COLUMN_NEXT;
+ }
+ else {
+ // Deleted next character. Go to direction segment of deleted character
+ lastCaretDirection = ST.COLUMN_PREVIOUS;
+ }
+ }
+ else {
+ if (isBackspace) {
+ // Delete previous character inside direction segment (i.e., not at a direction boundary)
+ lastCaretDirection = ST.COLUMN_PREVIOUS;
+ }
+ else {
+ // Deleted next character.
+ lastCaretDirection = ST.COLUMN_NEXT;
+ }
+ }
+}
+/**
+ * Updates the selection and caret position depending on the text change.
+ * 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
+ * it is left unchanged.
+ * <p>
+ *
+ * @param startOffset offset of the text change
+ * @param replacedLength length of text being replaced
+ * @param newLength length of new text
+ */
+void updateSelection(int startOffset, int replacedLength, int newLength) {
+ if (selection.y <= startOffset) {
+ // selection ends before text change
+ return;
+ }
+ if (selection.x < startOffset) {
+ // clear selection fragment before text change
+ internalRedrawRange(selection.x, startOffset - selection.x, true);
+ }
+ if (selection.y > startOffset + replacedLength && selection.x < startOffset + replacedLength) {
+ // clear selection fragment after text change.
+ // do this only when the selection is actually affected by the
+ // change. Selection is only affected if it intersects the change (1GDY217).
+ int netNewLength = newLength - replacedLength;
+ int redrawStart = startOffset + newLength;
+ internalRedrawRange(redrawStart, selection.y + netNewLength - redrawStart, true);
+ }
+ if (selection.y > startOffset && selection.x < startOffset + replacedLength) {
+ // selection intersects replaced text. set caret behind text change
+ internalSetSelection(startOffset + newLength, 0, true);
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+ }
+ else {
+ // move selection to keep same text selected
+ internalSetSelection(selection.x + newLength - replacedLength, selection.y - selection.x, true);
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+ }
+}
+/**
+ * Rewraps all lines
+ * <p>
+ *
+ * @param oldClientAreaWidth client area width before resize
+ * occurred
+ */
+void wordWrapResize(int oldClientAreaWidth) {
+ WrappedContent wrappedContent = (WrappedContent) content;
+ int newTopIndex;
+
+ // all lines are wrapped and no rewrap required if widget has already
+ // been visible, client area is now wider and visual (wrapped) line
+ // count equals logical line count.
+ if (oldClientAreaWidth != 0 && clientAreaWidth > oldClientAreaWidth &&
+ wrappedContent.getLineCount() == logicalContent.getLineCount()) {
+ return;
+ }
+ wrappedContent.wrapLines();
+
+ // adjust the top index so that top line remains the same
+ newTopIndex = content.getLineAtOffset(topOffset);
+ // topOffset is the beginning of the top line. therefore it
+ // needs to be adjusted because in a wrapped line this is also
+ // the end of the preceeding line.
+ if (newTopIndex < content.getLineCount() - 1 &&
+ topOffset == content.getOffsetAtLine(newTopIndex + 1)) {
+ newTopIndex++;
+ }
+ if (newTopIndex != topIndex) {
+ ScrollBar verticalBar = getVerticalBar();
+ // adjust index and pixel offset manually instead of calling
+ // setVerticalScrollOffset because the widget does not actually need
+ // to be scrolled. causes flash otherwise.
+ verticalScrollOffset += (newTopIndex - topIndex) * getVerticalIncrement();
+ // verticalScrollOffset may become negative if first line was
+ // partially visible and second line was top line. prevent this from
+ // happening to fix 8503.
+ if (verticalScrollOffset < 0) {
+ verticalScrollOffset = 0;
+ }
+ topIndex = newTopIndex;
+ topOffset = content.getOffsetAtLine(topIndex);
+ if (verticalBar != null) {
+ verticalBar.setSelection(verticalScrollOffset);
+ }
+ }
+ // caret may be on a different line after a rewrap.
+ // call setCaretLocation after fixing vertical scroll offset.
+ setCaretLocation();
+ // word wrap may have changed on one of the visible lines
+ super.redraw();
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java
index fb98cb65cc..982517ff2a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextBidi.java
@@ -1,1056 +1,1056 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2001, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import java.util.*;
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.internal.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * This class provides API for StyledText to implement bidirectional text
- * functions.
- * Objects of this class are created for a single line of text.
- */
-class StyledTextBidi {
- private GC gc;
- private int[] bidiSegments; // bidi text segments, each segment will be rendered separately
- private int[] renderPositions; // x position at which characters of the line are rendered, in visual order
- private int[] order; // reordering indices in logical order, iV=order[iL] (iV=visual index, iL=logical index),
- // if no character in a line needs reordering all iV and iL are the same.
- private int[] dx; // distance between character cells. in visual order. renderPositions[iV + 1] = renderPositions[iV] + dx[iV]
- private byte[] classBuffer; // the character types in logical order, see BidiUtil for the possible types
- private char[] glyphBuffer; // the glyphs in visual order as they will be rendered on screen.
-
- /**
- * This class describes a text segment of a single direction, either
- * left-to-right (L2R) or right-to-left (R2L).
- * Objects of this class are used by StyledTextBidi rendering methods
- * to render logically contiguous text segments that may be visually
- * discontiguous if they consist of different directions.
- */
- class DirectionRun {
- int logicalStart;
- int logicalEnd;
-
- DirectionRun(int logicalStart, int logicalEnd) {
- this.logicalStart = logicalStart;
- this.logicalEnd = logicalEnd;
- }
- int getVisualStart() {
- int visualStart = order[logicalStart];
- int visualEnd = order[logicalEnd];
- // the visualStart of a R2L direction run is actually
- // at the run's logicalEnd, answered as such since rendering
- // always occurs from L2R regardless of the text run's
- // direction
- if (visualEnd < visualStart) {
- visualStart = visualEnd;
- }
- return visualStart;
- }
- int getVisualEnd() {
- int visualStart = order[logicalStart];
- int visualEnd = order[logicalEnd];
- // the visualEnd of a R2L direction run is actually
- // at the run's logicalStart, answered as such since rendering
- // always occurs from L2R regardless of the text run's
- // direction
- if (visualEnd < visualStart) {
- visualEnd = visualStart;
- }
- return visualEnd;
- }
- int getRenderStartX() {
- return renderPositions[getVisualStart()];
- }
- int getRenderStopX() {
- int visualEnd = getVisualEnd();
-
- return renderPositions[visualEnd] + dx[visualEnd];
- }
- public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append("vStart,Stop:" + getVisualStart() + "," + getVisualEnd() + " lStart,Stop:" + logicalStart + "," + logicalEnd + " renderStart,Stop: " + getRenderStartX() + "," + getRenderStopX());
- return buf.toString();
- }
- }
-
-/**
- * Constructs an instance of this class for a line of text. The text
- * is reordered to reflect how it will be displayed.
- * <p>
- *
- * @param gc the GC to use for rendering and measuring of this line.
- * @param tabWidth tab width in number of spaces, used to calculate
- * tab stops
- * @param text line that bidi data should be calculated for
- * @param boldRanges bold text segments in the line, specified as
- * i=bold start,i+1=bold length
- * @param boldFont font that bold text will be rendered in, needed for
- * proper measuring of bold text segments.
- * @param offset text segments that should be measured and reordered
- * separately, may be needed to preserve the order of separate R2L
- * segments to each other. Must have at least two elements, 0 and the text
- * length.
- */
-public StyledTextBidi(GC gc, int tabWidth, String text, StyleRange[] ranges, Font boldFont, int[] offsets) {
- int length = text.length();
-
- this.gc = gc;
- bidiSegments = offsets;
- renderPositions = new int[length];
- order = new int[length];
- dx = new int[length];
- classBuffer = new byte[length];
- if (length == 0) {
- glyphBuffer = new char[0];
- }
- else {
- glyphBuffer = BidiUtil.getRenderInfo(gc, text, order, classBuffer, dx, 0, offsets);
- if (ranges != null) {
- // If the font supports characters shaping, break up the font style ranges based on
- // the specified bidi segments. Each bidi segment will be treated separately
- // for font style purposes.
- StyleRange[] segmentedRanges;
- if (isCharacterShaped(gc)) segmentedRanges = getSegmentedRangesFor(ranges);
- else segmentedRanges = ranges;
- Font normalFont = gc.getFont();
- gc.setFont(boldFont);
- for (int i = 0; i < segmentedRanges.length; i++) {
- StyleRange segmentedRange = segmentedRanges[i];
- int rangeStart = segmentedRange.start;
- int rangeLength = segmentedRange.length;
- // Font styled text needs to be processed so that the dx array reflects the styled
- // font.
- prepareFontStyledText(text, rangeStart, rangeLength);
- }
- gc.setFont(normalFont);
- }
- calculateTabStops(text, tabWidth);
- calculateRenderPositions();
- }
-}
-/**
- * Constructs an instance of this class for a line of text. This constructor
- * should be used when only ordering (not rendering) information is needed.
- * Only the class and order arrays will be filled during this call.
- * <p>
- *
- * @param gc the GC to use for rendering and measuring of this line.
- * @param text line that bidi data should be calculated for
- * @param offset text segments that should be measured and reordered
- * separately, may be needed to preserve the order of separate R2L
- * segments to each other
- */
-public StyledTextBidi(GC gc, String text, int[] offsets) {
- int length = text.length();
- this.gc = gc;
- bidiSegments = offsets;
- order = new int[length];
- classBuffer = new byte[length];
- BidiUtil.getOrderInfo(gc, text, order, classBuffer, 0, offsets);
- // initialize the unused arrays
- dx = new int[0];
- renderPositions = new int[0];
- glyphBuffer = new char[0];
-
-}
-/**
- * Adds a listener that should be called when the user changes the
- * keyboard layout for the specified window.
- * <p>
- *
- * @param control Control to add the keyboard language listener for.
- * Each window has its own keyboard language setting.
- * @param runnable the listener that should be called when the user
- * changes the keyboard layout.
- */
-static void addLanguageListener(Control control, Runnable runnable) {
- BidiUtil.addLanguageListener(control.handle, runnable);
-}
-/**
- * Answers the direction of the active keyboard language - either
- * L2R or R2L. The active keyboard language determines the direction
- * of the caret and can be changed by the user (e.g., via Alt-Shift on
- * Win32 platforms).
- * <p>
- *
- * @return the direction of the active keyboard language. SWT.LEFT (for L2R
- * language) or SWT.RIGHT (for R2L language) or SWT.DEFAULT if no R2L languages
- * are installed.
- */
-static int getKeyboardLanguageDirection() {
- int language = BidiUtil.getKeyboardLanguage();
- if (language == BidiUtil.KEYBOARD_BIDI) {
- return SWT.RIGHT;
- }
- if (BidiUtil.isKeyboardBidi()) {
- return SWT.LEFT;
- }
- return SWT.DEFAULT;
-}
-/**
- * Returns whether the current platform supports a bidi language.
- * <p>
- *
- * @return true=bidi is supported, false otherwise.
- */
-static boolean isBidiPlatform() {
- return BidiUtil.isBidiPlatform();
-}
-/**
- * Returns whether the font set in the specified gc supports
- * character shaping.
- * <p>
- *
- * @param gc the GC that should be tested for character shaping.
- * @return
- * true=the font set in the specified gc supports character shaped glyphs
- * false=the font set in the specified gc doesn't support character shaped glyphs
- */
-static boolean isCharacterShaped(GC gc) {
- return (BidiUtil.getFontBidiAttributes(gc) & BidiUtil.GLYPHSHAPE) != 0;
-}
-/**
- * Returns whether the font set in the specified gc contains
- * ligatured glyphs.
- * <p>
- *
- * @param gc the GC that should be tested for ligatures.
- * @return
- * true=the font set in the specified gc contains ligatured glyphs.
- * false=the font set in the specified gc doesn't contain ligatured
- * glyphs.
- */
-static boolean isLigated(GC gc) {
- return (BidiUtil.getFontBidiAttributes(gc) & BidiUtil.LIGATE) != 0;
-}
-/**
- * Removes the keyboard language listener for the specified window.
- * <p>
- *
- * @param control window to remove the keyboard language listener from.
- */
-static void removeLanguageListener(Control control) {
- BidiUtil.removeLanguageListener(control.handle);
-}
-/**
- * Calculates render positions using the glyph distance values in the dx array.
- */
-private void calculateRenderPositions() {
- renderPositions = new int[dx.length];
- renderPositions[0] = StyledText.XINSET;
- for (int i = 0; i < dx.length - 1; i++) {
- renderPositions[i + 1] = renderPositions[i] + dx[i];
- }
-}
-/**
- * Calculate the line's tab stops and adjust the dx array to
- * reflect the width of tab characters.
- * <p>
- *
- * @param text the original line text (not reordered) containing
- * tab characters.
- * @param tabWidth number of pixels that one tab character represents
- */
-private void calculateTabStops(String text, int tabWidth) {
- int tabIndex = text.indexOf('\t', 0);
- int logicalIndex = 0;
- int x = 0;
- int spaceWidth = gc.stringExtent(" ").x;
-
- while (tabIndex != -1) {
- for (; logicalIndex < tabIndex; logicalIndex++) {
- x += dx[order[logicalIndex]];
- }
- int tabStop = x + tabWidth;
- // make sure tab stop is at least one space width apart
- // from the last character. fixes 4844.
- if (tabWidth - tabStop % tabWidth < spaceWidth) {
- tabStop += tabWidth;
- }
- tabStop -= tabStop % tabWidth;
- dx[order[tabIndex]] = tabStop - x;
- tabIndex = text.indexOf('\t', tabIndex + 1);
- }
-}
-/**
- * Renders the specified text segment. All text is rendered L2R
- * regardless of the direction of the text. The rendered text may
- * be visually discontiguous if the text segment is bidirectional.
- * <p>
- *
- * @param logicalStart start offset in the logical text
- * @param length number of logical characters to render
- * @param xOffset x location of the line start
- * @param yOffset y location of the line start
- */
-void drawBidiText(int logicalStart, int length, int xOffset, int yOffset) {
- Enumeration directionRuns;
- int endOffset = logicalStart + length;
-
- if (logicalStart < 0 || endOffset > getTextLength()) {
- return;
- }
- directionRuns = getDirectionRuns(logicalStart, length).elements();
- while (directionRuns.hasMoreElements()) {
- DirectionRun run = (DirectionRun) directionRuns.nextElement();
- int visualStart = run.getVisualStart();
- int visualEnd = run.getVisualEnd();
- int x = xOffset + run.getRenderStartX();
- drawGlyphs(visualStart, visualEnd - visualStart + 1, x, yOffset);
- }
-}
-/**
- * Renders a segment of glyphs. Glyphs are visual objects so the
- * start and length are visual as well. Glyphs are always rendered L2R.
- * <p>
- *
- * @param visualStart start offset of the glyphs to render relative to the
- * line start.
- * @param length number of glyphs to render
- * @param x x location to render at
- * @param y y location to render at
- */
-private void drawGlyphs(int visualStart, int length, int x, int y) {
- char[] renderBuffer = new char[length];
- int[] renderDx = new int[length];
- if (length == 0) {
- return;
- }
- System.arraycopy(glyphBuffer, visualStart, renderBuffer, 0, length);
- // copy the distance values for the desired rendering range
- System.arraycopy(dx, visualStart, renderDx, 0, length);
- BidiUtil.drawGlyphs(gc, renderBuffer, renderDx, x, y);
-}
-/**
- * Fills a rectangle spanning the given logical range.
- * The rectangle may be visually discontiguous if the text segment
- * is bidirectional.
- * <p>
- *
- * @param logicalStart logcial start offset of the rectangle
- * @param length number of logical characters the rectangle should span
- * @param xOffset x location of the line start
- * @param yOffset y location of the line start
- * @param height height of the rectangle
- */
-void fillBackground(int logicalStart, int length, int xOffset, int yOffset, int height) {
- Enumeration directionRuns = getDirectionRuns(logicalStart, length).elements();
-
- if (logicalStart < 0 || logicalStart + length > getTextLength()) {
- return;
- }
- while (directionRuns.hasMoreElements()) {
- DirectionRun run = (DirectionRun) directionRuns.nextElement();
- int startX = run.getRenderStartX();
- gc.fillRectangle(xOffset + startX, yOffset, run.getRenderStopX() - startX, height);
- }
-}
-/**
- * Returns the offset and direction that will be used to position the caret for
- * the given x location. The caret will be placed in front of or behind the
- * character at location x depending on what type of character (i.e., R2L or L2R)
- * is at location x. This method is used for positioning the caret when a mouse
- * click occurs within the widget.
- * <p>
- *
- * @param x the x location of the character in the line.
- * @return array containing the caret offset and direction for the x location.
- * index 0: offset relative to the start of the line
- * index 1: direction, either ST.COLUMN_NEXT or ST.COLUMN_PREVIOUS.
- * The direction is used to control the caret position at direction
- * boundaries. The semantics follow the behavior for keyboard cursor
- * navigation.
- * Example: RRRLLL
- * Pressing cursor left (COLUMN_PREVIOUS) in the L2R segment places the cursor
- * in front of the first character of the L2R segment. Pressing cursor right
- * (COLUMN_NEXT) in a R2L segment places the cursor behind the last character
- * of the R2L segment. However, both are the same logical offset.
- */
-int[] getCaretOffsetAndDirectionAtX(int x) {
- int lineLength = getTextLength();
- int offset;
- int direction;
-
- if (lineLength == 0) {
- return new int[] {0, 0};
- }
- int eol = renderPositions[renderPositions.length - 1] + dx[dx.length - 1];
- if (x >= eol) {
- return new int[] {lineLength, ST.COLUMN_NEXT};
- }
- // get the visual offset of the clicked character
- int visualOffset = getVisualOffsetAtX(x);
- // figure out if the character was clicked on the right or left
- int halfway = renderPositions[visualOffset] + dx[visualOffset] / 2;
- boolean visualLeft = (x <= halfway);
- offset = getLogicalOffset(visualOffset);
-
- if (isRightToLeft(offset)) {
- if (visualLeft) {
- if (isLigated(gc)) {
- // the caret should be positioned after the last
- // character of the ligature
- offset = getLigatureEndOffset(offset);
- }
- offset++;
- // position the caret as if the caret is to the right
- // of the character at location x and the NEXT key is
- // pressed
- direction = ST.COLUMN_NEXT;
- }
- else {
- // position the caret as if the caret is to the left
- // of the character at location x and the PREVIOUS key is
- // pressed
- direction = ST.COLUMN_PREVIOUS;
- }
- }
- else {
- if (visualLeft) {
- // position the caret as if the caret is to the right
- // of the character at location x and the PREVIOUS key is
- // pressed
- direction = ST.COLUMN_PREVIOUS;
- }
- else {
- // position the caret as if the caret is to the left
- // of the character at location x and the NEXT key is
- // pressed
- offset++;
- direction = ST.COLUMN_NEXT;
- }
- }
- return new int[] {offset, direction};
-}
-/**
- * Returns the direction segments that are in the specified text
- * range. The text range may be visually discontiguous if the
- * text is bidirectional. Each returned direction run has a single
- * direction and the runs all go from left to right, regardless of
- * the direction of the text in the segment. User specified segments
- * (via BidiSegmentListener) are taken into account and result in
- * separate direction runs.
- * <p>
- *
- * @param logicalStart offset of the logcial start of the first
- * direction segment
- * @param length length of the text included in the direction
- * segments
- * @return the direction segments that are in the specified
- * text range, each segment has a single direction.
- */
-private Vector getDirectionRuns(int logicalStart, int length) {
- Vector directionRuns = new Vector();
- int logicalEnd = logicalStart + length - 1;
- int segmentLogicalStart = logicalStart;
- int segmentLogicalEnd = segmentLogicalStart;
-
- if (logicalEnd < getTextLength()) {
- int bidiSegmentIndex = 0;
- int bidiSegmentEnd = bidiSegments[bidiSegmentIndex + 1];
-
- // Find the bidi segment that the direction runs start in.
- // There will always be at least on bidi segment (for the entire line).
- while (bidiSegmentIndex < bidiSegments.length - 2 && bidiSegmentEnd <= logicalStart) {
- bidiSegmentIndex++;
- bidiSegmentEnd = bidiSegments[bidiSegmentIndex + 1];
- }
- while (segmentLogicalEnd <= logicalEnd) {
- boolean isRightToLeftSegment = isRightToLeft(segmentLogicalStart);
- // Search for the end of the direction segment. Each segment needs to
- // be rendered separately.
- // E.g., 11211 (1=R2L, 2=L2R), rendering from logical index 0 to 5
- // would be visual 1 to 4 and would thus miss visual 0. Rendering the
- // segments separately would render from visual 1 to 0, then 2, then
- // 4 to 3.
- while (segmentLogicalEnd < logicalEnd &&
- // If our segment type is RtoL, the order index for the next character should be one less, if there
- // is no direction change.
- // If our segment type is LtoR, the order index for the next character will be one more if there is
- // no direction change.
- ((isRightToLeftSegment && (order[segmentLogicalEnd + 1]+ 1 == order[segmentLogicalEnd])) ||
- (isRightToLeftSegment == false && (order[segmentLogicalEnd + 1]- 1 == order[segmentLogicalEnd]))) &&
- segmentLogicalEnd + 1 < bidiSegmentEnd) {
- segmentLogicalEnd++;
- }
- directionRuns.addElement(new DirectionRun(segmentLogicalStart, segmentLogicalEnd));
- segmentLogicalStart = ++segmentLogicalEnd;
- // The current direction run ends at a bidi segment end. Get the next bidi segment.
- if (segmentLogicalEnd == bidiSegmentEnd && bidiSegmentIndex < bidiSegments.length - 2) {
- bidiSegmentIndex++;
- bidiSegmentEnd = bidiSegments[bidiSegmentIndex + 1];
- }
- }
- }
- return directionRuns;
-}
-/**
- * Returns the offset of the last character comprising a ligature.
- * <p>
- *
- * @param offset the logical offset of a character that may be a
- * ligature.
- * @return the offset of the last character comprising a ligature.
- */
-int getLigatureEndOffset(int offset) {
- int newOffset = offset;
- int i = offset + 1;
-
- // assume only bidi languages support ligatures
- if (offset < 0 || offset >= order.length || isRightToLeft(offset) == false) {
- return offset;
- }
- // a ligature is a visual character that is comprised of
- // multiple logical characters, thus each logical part of
- // a ligature will have the same order value
- while (i < order.length && (order[i] == order[offset])) {
- newOffset = i;
- i++;
- }
- return newOffset;
-}
-/**
- * Returns the offset of the first character comprising a ligature.
- * <p>
- *
- * @param offset the logical offset of a character that may be a
- * ligature.
- * @return the offset of the first character comprising a ligature.
- */
-int getLigatureStartOffset(int offset) {
- int newOffset = offset;
- int i = offset - 1;
-
- // assume only bidi languages support ligatures
- if (offset < 0 || offset >= order.length || isRightToLeft(offset) == false) {
- return offset;
- }
- // a ligature is a visual character that is comprised of
- // multiple logical characters, thus each logical part of
- // a ligature will have the same order value
- while (i >= 0 && (order[i] == order[offset])) {
- newOffset = i;
- i--;
- }
- return newOffset;
-}
-/**
- * Returns the logical offset of the character at the specified
- * visual offset.
- * <p>
- *
- * @param visualOffset the visual offset
- * @return the logical offset of the character at <code>visualOffset</code>.
- */
-private int getLogicalOffset(int visualOffset) {
- int logicalOffset = 0;
-
- while (logicalOffset < order.length && order[logicalOffset] != visualOffset) {
- logicalOffset++;
- }
- return logicalOffset;
-}
-/**
- * Returns the offset of the character at the specified x location.
- * <p>
- *
- * @param x the location of the character
- * @return the logical offset of the character at the specified x
- * location.
- */
-int getOffsetAtX(int x) {
- int visualOffset;
-
- if (getTextLength() == 0) {
- return 0;
- }
- if (x >= renderPositions[renderPositions.length - 1] + dx[dx.length - 1]) {
- // Return when x is past the end of the line. Fixes 1GLADBK.
- return -1;
- }
- visualOffset = getVisualOffsetAtX(x);
- return getLogicalOffset(visualOffset);
-}
-/**
- * Returns the reordering indices that map between logical and
- * visual index of characters in the specified range.
- * <p>
- *
- * @param start start offset of the reordering indices
- * @param length number of reordering indices to return
- * @return the reordering indices that map between logical and
- * visual index of characters in the specified range. Relative
- * to the start of the range.
- */
-private int[] getRenderIndexesFor(int start, int length) {
- int[] positions = new int[length];
- int end = start + length;
-
- for (int i = start; i < end; i++) {
- positions[i-start] = order[i];
- }
- return positions;
-}
-/**
- * Break up the given ranges such that each range is fully contained within a bidi
- * segment.
- */
-private StyleRange[] getSegmentedRangesFor(StyleRange[] ranges) {
- if ((bidiSegments == null) || (bidiSegments.length == 0)) return ranges;
- Vector newRanges = new Vector();
- int j=0;
- int startSegment;
- int endSegment;
- for (int i=0; i<ranges.length; i++) {
- int start = ranges[i].start;
- int end = start+ranges[i].length;
- startSegment=-1;
- endSegment=-1;
- boolean done = false;
- while (j<bidiSegments.length && !done) {
- if (bidiSegments[j]<=start) {
- startSegment=j;
- }
- if (bidiSegments[j]>=end) {
- endSegment=j-1;
- j--;
- }
- done = (startSegment != -1) && (endSegment != -1);
- if (!done) j++;
- }
- if (startSegment == endSegment) {
- // range is within one segment
- StyleRange newStyle = new StyleRange(start, end-start, null, null);
- newRanges.addElement(newStyle);
- } else if (startSegment > endSegment) {
- // range is within no segment (i.e., it's empty)
- } else {
- // range spans multiple segments
- StyleRange newStyle = new StyleRange(start, bidiSegments[startSegment+1]-start, null, null);
- newRanges.addElement(newStyle);
- startSegment++;
- for (int k=startSegment; k<endSegment; k++) {
- newStyle = new StyleRange(bidiSegments[k], bidiSegments[k+1]-bidiSegments[k], null, null);
- newRanges.addElement(newStyle);
- }
- newStyle = new StyleRange(bidiSegments[endSegment], end-bidiSegments[endSegment], null, null);
- newRanges.addElement(newStyle);
- }
- }
- StyleRange[] rangeArray = new StyleRange[newRanges.size()];
- for (int i=0; i<newRanges.size(); i++) {
- rangeArray[i]=(StyleRange)newRanges.elementAt(i);
- }
- return rangeArray;
-}
-/**
- * Returns the number of characters in the line.
- * <p>
- *
- * @return the number of characters in the line.
- */
-private int getTextLength() {
- return dx.length;
-}
-/**
- * Returns the x position at the specified offset in the line.
- * <p>
- * @param logicalOffset offset of the character in the line.
- * @return the x position at the specified offset in the line.
- */
-int getTextPosition(int logicalOffset) {
- return getTextPosition(logicalOffset, ST.COLUMN_NEXT);
-}
-/**
- * Returns the x position at the specified offset in the line.
- * The direction parameter is used to determine the position
- * at direction boundaries. If the logical offset is between a R2L
- * and a L2R segment, pressing cursor left in the L2R segment places
- * the position in front of the first character of the L2R segment; whereas
- * pressing cursor right in the R2L segment places the position behind
- * the last character of the R2L segment. However, both x positions
- * are at the same logical offset.
- * <p>
- *
- * @param logicalOffset offset of the character in the line
- * @param direction direction the caret moved to the specified location.
- * either ST.COLUMN_NEXT (right cursor key) or ST.COLUMN_PREVIOUS (left cursor key) .
- * @return the x position at the specified offset in the line,
- * taking the direction into account as described above.
- */
-int getTextPosition(int logicalOffset, int direction) {
- int caretX;
-
- if (getTextLength() == 0 || logicalOffset < 0) {
- return StyledText.XINSET;
- }
- // at or past end of line?
- if (logicalOffset >= order.length) {
- logicalOffset = Math.min(logicalOffset, order.length - 1);
- int visualOffset = order[logicalOffset];
- if (isRightToLeft(logicalOffset)) {
- caretX = renderPositions[visualOffset];
- }
- else {
- caretX = renderPositions[visualOffset] + dx[visualOffset];
- }
- }
- else
- // at beginning of line?
- if (logicalOffset == 0) {
- int visualOffset = order[logicalOffset];
- if (isRightToLeft(logicalOffset)) {
- caretX = renderPositions[visualOffset] + dx[visualOffset];
- }
- else {
- caretX = renderPositions[visualOffset];
- }
- }
- else
- // always consider local numbers as a direction boundary
- // because they represent a discontiguous text segment coming from
- // a R2L segment.
- // treat user specified direction segments like real direction changes.
- if (direction == ST.COLUMN_NEXT &&
- (isRightToLeft(logicalOffset) != isRightToLeft(logicalOffset - 1) ||
- isLocalNumber(logicalOffset) != isLocalNumber(logicalOffset - 1) ||
- isStartOfBidiSegment(logicalOffset))) {
- int visualOffset = order[logicalOffset-1];
- // moving between segments.
- // do not consider local numbers as R2L here, to determine position,
- // because local numbers are navigated L2R and we want the caret to
- // be to the right of the number. see 1GK9API
- if (isRightToLeft(logicalOffset - 1)) {
- // moving from RtoL to LtoR
- caretX = renderPositions[visualOffset];
- }
- else {
- // moving from LtoR to RtoL
- caretX = renderPositions[visualOffset] + dx[visualOffset];
- }
- }
- else
- // consider local numbers as R2L in determining direction boundaries.
- // fixes 1GK9API.
- if (direction == ST.COLUMN_PREVIOUS &&
- isRightToLeftInput(logicalOffset) != isRightToLeftInput(logicalOffset - 1)) {
- int visualOffset = order[logicalOffset];
- // moving between segments.
- // consider local numbers as R2L here, to determine position, because
- // we want to stay in L2R segment and place the cursor to the left of
- // first L2R character. see 1GK9API
- if (isRightToLeftInput(logicalOffset - 1)) {
- // moving from LtoR to RtoL
- caretX = renderPositions[visualOffset];
- }
- else {
- // moving from RtoL to LtoR
- caretX = renderPositions[visualOffset] + dx[visualOffset];
- }
- }
- else
- if (isRightToLeft(logicalOffset)) {
- int visualOffset = order[logicalOffset];
- caretX = renderPositions[visualOffset] + dx[visualOffset];
- }
- else {
- caretX = renderPositions[order[logicalOffset]];
- }
- return caretX;
-}
-/**
- * Returns the width in pixels of the line.
- * <p>
- *
- * @return the width in pixels of the line.
- */
-int getTextWidth() {
- int width = 0;
-
- if (getTextLength() > 0) {
- width = renderPositions[renderPositions.length - 1] + dx[dx.length - 1];
- }
- return width;
-}
-/**
- * Returns the visual offset of the character at the specified x
- * location.
- * <p>
- *
- * @param x the location of the character
- * @return the visual offset of the character at the specified x
- * location.
- */
-private int getVisualOffsetAtX(int x) {
- int lineLength = getTextLength();
- int low = -1;
- int high = lineLength;
-
- while (high - low > 1) {
- int offset = (high + low) / 2;
- int visualX = renderPositions[offset];
-
- // visualX + dx is the start of the next character. Restrict right/high
- // search boundary only if x is before next character. Fixes 1GL4ZVE.
- if (x < visualX + dx[offset]) {
- high = offset;
- }
- else
- if (high == lineLength && high - offset == 1) {
- // requested x location is past end of line
- high = -1;
- }
- else {
- low = offset;
- }
- }
- return high;
-}
-/**
- * Returns if the character at the given offset is a local number.
- * <p>
- *
- * @param logicalIndex the index of the character
- * @return
- * true=the character at the specified index is a local number
- * false=the character at the specified index is not a local number
- */
-boolean isLocalNumber(int logicalIndex) {
- boolean isLocalNumber = false;
-
- if (logicalIndex >= 0 && logicalIndex < classBuffer.length) {
- isLocalNumber = classBuffer[logicalIndex] == BidiUtil.CLASS_LOCALNUMBER;
- }
- return isLocalNumber;
-}
-/**
- * Returns the direction of the character at the specified index.
- * Used for rendering and caret positioning where local numbers (e.g.,
- * national Arabic, or Hindi, numbers) are considered left-to-right.
- * <p>
- *
- * @param logicalIndex the index of the character
- * @return
- * true=the character at the specified index is in a right-to-left
- * codepage (e.g., Hebrew, Arabic).
- * false=the character at the specified index is in a left-to-right/latin
- * codepage.
- */
-boolean isRightToLeft(int logicalIndex) {
- boolean isRightToLeft = false;
-
- if (logicalIndex >= 0 && logicalIndex < classBuffer.length) {
- isRightToLeft = (classBuffer[logicalIndex] == BidiUtil.CLASS_ARABIC) ||
- (classBuffer[logicalIndex] == BidiUtil.CLASS_HEBREW);
- }
- return isRightToLeft;
-}
-/**
- * Returns the direction of the character at the specified index.
- * Used for setting the keyboard language where local numbers (e.g.,
- * national Arabic, or Hindi, numbers) are considered right-to-left.
- * <p>
- *
- * @param logicalIndex the index of the character
- * @return
- * true=the character at the specified index is in a right-to-left
- * codepage (e.g., Hebrew, Arabic).
- * false=the character at the specified index is in a left-to-right/latin
- * codepage.
- */
-boolean isRightToLeftInput(int logicalIndex) {
- boolean isRightToLeft = false;
-
- if (logicalIndex >= 0 && logicalIndex < classBuffer.length) {
- isRightToLeft = (classBuffer[logicalIndex] == BidiUtil.CLASS_ARABIC) ||
- (classBuffer[logicalIndex] == BidiUtil.CLASS_HEBREW) ||
- (classBuffer[logicalIndex] == BidiUtil.CLASS_LOCALNUMBER);
- }
- return isRightToLeft;
-}
-/**
- * Returns whether the specified index is the start of a user
- * specified direction segment.
- * <p>
- *
- * @param logicalIndex the index to test
- * @return true=the specified index is the start of a user specified
- * direction segment, false otherwise
- */
-private boolean isStartOfBidiSegment(int logicalIndex) {
- for (int i = 0; i < bidiSegments.length; i++) {
- if (bidiSegments[i] == logicalIndex) return true;
- }
- return false;
-}
-/**
- * Reorders and calculates render positions for the specified sub-line
- * of text. The results will be merged with the data for the rest of
- * the line .
- * <p>
- *
- * @param textline the entire line of text that this object represents.
- * @param logicalStart the start offset of the first character to
- * reorder.
- * @param length the number of characters to reorder
- */
-private void prepareFontStyledText(String textline, int logicalStart, int length) {
- int byteCount = length;
- int flags = 0;
- String text = textline.substring(logicalStart, logicalStart + length);
-
- // Figure out what is before and after the substring so that the proper character
- // shaping will occur. Character shaping will not occur across bidi segments, so
- // if the styled text starts or ends on a bidi segment, do not process the text
- // for character shaping.
- if (logicalStart != 0
- && isCharacterShaped(gc)
- && !isStartOfBidiSegment(logicalStart)
- && !Compatibility.isWhitespace(textline.charAt(logicalStart - 1))
- && isRightToLeft(logicalStart - 1)) {
- // if the start of the substring is not the beginning of the
- // text line, check to see what is before the string
- flags |= BidiUtil.LINKBEFORE;
- }
- if ((logicalStart + byteCount) != dx.length
- && isCharacterShaped(gc)
- && !isStartOfBidiSegment(logicalStart + length)
- && !Compatibility.isWhitespace(textline.charAt(logicalStart + byteCount))
- && isRightToLeft(logicalStart + byteCount)) {
- // if the end of the substring is not the end of the text line,
- // check to see what is after the substring
- flags |= BidiUtil.LINKAFTER;
- }
- // set classification values for the substring
- flags |= BidiUtil.CLASSIN;
- byte[] classArray = new byte[byteCount];
- int[] renderIndexes = getRenderIndexesFor(logicalStart, byteCount);
- for (int i = 0; i < byteCount; i++) {
- classArray[i] = classBuffer[renderIndexes[i]];
- }
- int[] dxArray = new int[byteCount];
- int[] orderArray = new int[byteCount];
- BidiUtil.getRenderInfo(gc, text, orderArray, classArray, dxArray, flags, new int[] {0, text.length()});
- // update the existing dx array with the new dx values based on the bold font
- for (int i = 0; i < dxArray.length; i++) {
- int dxValue = dxArray[orderArray[i]];
- int visualIndex = renderIndexes[i];
- dx[visualIndex] = dxValue;
- }
-}
-/**
- * Redraws a rectangle spanning the given logical range.
- * The rectangle may be visually discontiguous if the text segment
- * is bidirectional.
- * <p>
- *
- * @param parent window that should be invalidated
- * @param logicalStart logcial start offset of the rectangle
- * @param length number of logical characters the rectangle should span
- * @param xOffset x location of the line start
- * @param yOffset y location of the line start
- * @param height height of the invalidated rectangle
- */
-void redrawRange(Control parent, int logicalStart, int length, int xOffset, int yOffset, int height) {
- Enumeration directionRuns;
-
- if (logicalStart < 0 || logicalStart + length > getTextLength()) {
- return;
- }
- directionRuns = getDirectionRuns(logicalStart, length).elements();
- while (directionRuns.hasMoreElements()) {
- DirectionRun run = (DirectionRun) directionRuns.nextElement();
- int startX = run.getRenderStartX();
-
- parent.redraw(xOffset + startX, yOffset, run.getRenderStopX() - startX, height, true);
- }
-}
-/**
- * Sets the keyboard language to match the codepage of the character
- * at the specified offset.
- * Only distinguishes between left-to-right and right-to-left characters and
- * sets the keyboard language to a bidi or non-bidi language.
- * <p>
- *
- * @param logicalIndex logical offset of the character to use for
- * determining the new keyboard language.
- */
-void setKeyboardLanguage(int logicalIndex) {
- int language;
- int current = BidiUtil.getKeyboardLanguage();
-
- if (logicalIndex < 0 || logicalIndex >= classBuffer.length) {
- return;
- }
- if (isRightToLeftInput(logicalIndex)) {
- // keyboard already in bidi mode, since we cannot distinguish between
- // multiple bidi languages, just return
- if (current == BidiUtil.KEYBOARD_BIDI) return;
- language = BidiUtil.KEYBOARD_BIDI;
- } else {
- // keyboard already in non-bidi mode, since we cannot distinguish between
- // multiple non-bidi languages, just return
- if (current == BidiUtil.KEYBOARD_NON_BIDI) return;
- language = BidiUtil.KEYBOARD_NON_BIDI;
- }
- BidiUtil.setKeyboardLanguage(language);
-}
-/**
- * Returns a string representation of the receiver.
- * <p>
- *
- * @return a string representation of the receiver for
- * debugging purposes. The output order of the StyledTextbidi values
- * is as follows: order, render position, dx, character class, glyphs.
- */
-public String toString() {
- StringBuffer buf = new StringBuffer();
-
- buf.append("StyledTextBidi {{");
- // order
- for (int i = 0; i < order.length; i++) {
- if (i != 0) {
- buf.append(",");
- }
- buf.append(order[i]);
- }
- buf.append("}, {");
- // render positions
- for (int i = 0; i < renderPositions.length; i++) {
- if (i != 0) {
- buf.append(",");
- }
- buf.append(renderPositions[i]);
- }
- buf.append("}, {");
- // dx
- for (int i = 0; i < dx.length; i++) {
- if (i != 0) {
- buf.append(",");
- }
- buf.append(dx[i]);
- }
- buf.append("}, {");
- // character class
- for (int i = 0; i < classBuffer.length; i++) {
- if (i != 0) {
- buf.append(",");
- }
- buf.append(classBuffer[i]);
- }
- buf.append("}, {");
- // glyphs
- buf.append(glyphBuffer);
- buf.append("}}");
- return buf.toString();
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import java.util.*;
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * This class provides API for StyledText to implement bidirectional text
+ * functions.
+ * Objects of this class are created for a single line of text.
+ */
+class StyledTextBidi {
+ private GC gc;
+ private int[] bidiSegments; // bidi text segments, each segment will be rendered separately
+ private int[] renderPositions; // x position at which characters of the line are rendered, in visual order
+ private int[] order; // reordering indices in logical order, iV=order[iL] (iV=visual index, iL=logical index),
+ // if no character in a line needs reordering all iV and iL are the same.
+ private int[] dx; // distance between character cells. in visual order. renderPositions[iV + 1] = renderPositions[iV] + dx[iV]
+ private byte[] classBuffer; // the character types in logical order, see BidiUtil for the possible types
+ private char[] glyphBuffer; // the glyphs in visual order as they will be rendered on screen.
+
+ /**
+ * This class describes a text segment of a single direction, either
+ * left-to-right (L2R) or right-to-left (R2L).
+ * Objects of this class are used by StyledTextBidi rendering methods
+ * to render logically contiguous text segments that may be visually
+ * discontiguous if they consist of different directions.
+ */
+ class DirectionRun {
+ int logicalStart;
+ int logicalEnd;
+
+ DirectionRun(int logicalStart, int logicalEnd) {
+ this.logicalStart = logicalStart;
+ this.logicalEnd = logicalEnd;
+ }
+ int getVisualStart() {
+ int visualStart = order[logicalStart];
+ int visualEnd = order[logicalEnd];
+ // the visualStart of a R2L direction run is actually
+ // at the run's logicalEnd, answered as such since rendering
+ // always occurs from L2R regardless of the text run's
+ // direction
+ if (visualEnd < visualStart) {
+ visualStart = visualEnd;
+ }
+ return visualStart;
+ }
+ int getVisualEnd() {
+ int visualStart = order[logicalStart];
+ int visualEnd = order[logicalEnd];
+ // the visualEnd of a R2L direction run is actually
+ // at the run's logicalStart, answered as such since rendering
+ // always occurs from L2R regardless of the text run's
+ // direction
+ if (visualEnd < visualStart) {
+ visualEnd = visualStart;
+ }
+ return visualEnd;
+ }
+ int getRenderStartX() {
+ return renderPositions[getVisualStart()];
+ }
+ int getRenderStopX() {
+ int visualEnd = getVisualEnd();
+
+ return renderPositions[visualEnd] + dx[visualEnd];
+ }
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("vStart,Stop:" + getVisualStart() + "," + getVisualEnd() + " lStart,Stop:" + logicalStart + "," + logicalEnd + " renderStart,Stop: " + getRenderStartX() + "," + getRenderStopX());
+ return buf.toString();
+ }
+ }
+
+/**
+ * Constructs an instance of this class for a line of text. The text
+ * is reordered to reflect how it will be displayed.
+ * <p>
+ *
+ * @param gc the GC to use for rendering and measuring of this line.
+ * @param tabWidth tab width in number of spaces, used to calculate
+ * tab stops
+ * @param text line that bidi data should be calculated for
+ * @param boldRanges bold text segments in the line, specified as
+ * i=bold start,i+1=bold length
+ * @param boldFont font that bold text will be rendered in, needed for
+ * proper measuring of bold text segments.
+ * @param offset text segments that should be measured and reordered
+ * separately, may be needed to preserve the order of separate R2L
+ * segments to each other. Must have at least two elements, 0 and the text
+ * length.
+ */
+public StyledTextBidi(GC gc, int tabWidth, String text, StyleRange[] ranges, Font boldFont, int[] offsets) {
+ int length = text.length();
+
+ this.gc = gc;
+ bidiSegments = offsets;
+ renderPositions = new int[length];
+ order = new int[length];
+ dx = new int[length];
+ classBuffer = new byte[length];
+ if (length == 0) {
+ glyphBuffer = new char[0];
+ }
+ else {
+ glyphBuffer = BidiUtil.getRenderInfo(gc, text, order, classBuffer, dx, 0, offsets);
+ if (ranges != null) {
+ // If the font supports characters shaping, break up the font style ranges based on
+ // the specified bidi segments. Each bidi segment will be treated separately
+ // for font style purposes.
+ StyleRange[] segmentedRanges;
+ if (isCharacterShaped(gc)) segmentedRanges = getSegmentedRangesFor(ranges);
+ else segmentedRanges = ranges;
+ Font normalFont = gc.getFont();
+ gc.setFont(boldFont);
+ for (int i = 0; i < segmentedRanges.length; i++) {
+ StyleRange segmentedRange = segmentedRanges[i];
+ int rangeStart = segmentedRange.start;
+ int rangeLength = segmentedRange.length;
+ // Font styled text needs to be processed so that the dx array reflects the styled
+ // font.
+ prepareFontStyledText(text, rangeStart, rangeLength);
+ }
+ gc.setFont(normalFont);
+ }
+ calculateTabStops(text, tabWidth);
+ calculateRenderPositions();
+ }
+}
+/**
+ * Constructs an instance of this class for a line of text. This constructor
+ * should be used when only ordering (not rendering) information is needed.
+ * Only the class and order arrays will be filled during this call.
+ * <p>
+ *
+ * @param gc the GC to use for rendering and measuring of this line.
+ * @param text line that bidi data should be calculated for
+ * @param offset text segments that should be measured and reordered
+ * separately, may be needed to preserve the order of separate R2L
+ * segments to each other
+ */
+public StyledTextBidi(GC gc, String text, int[] offsets) {
+ int length = text.length();
+ this.gc = gc;
+ bidiSegments = offsets;
+ order = new int[length];
+ classBuffer = new byte[length];
+ BidiUtil.getOrderInfo(gc, text, order, classBuffer, 0, offsets);
+ // initialize the unused arrays
+ dx = new int[0];
+ renderPositions = new int[0];
+ glyphBuffer = new char[0];
+
+}
+/**
+ * Adds a listener that should be called when the user changes the
+ * keyboard layout for the specified window.
+ * <p>
+ *
+ * @param control Control to add the keyboard language listener for.
+ * Each window has its own keyboard language setting.
+ * @param runnable the listener that should be called when the user
+ * changes the keyboard layout.
+ */
+static void addLanguageListener(Control control, Runnable runnable) {
+ BidiUtil.addLanguageListener(control.handle, runnable);
+}
+/**
+ * Answers the direction of the active keyboard language - either
+ * L2R or R2L. The active keyboard language determines the direction
+ * of the caret and can be changed by the user (e.g., via Alt-Shift on
+ * Win32 platforms).
+ * <p>
+ *
+ * @return the direction of the active keyboard language. SWT.LEFT (for L2R
+ * language) or SWT.RIGHT (for R2L language) or SWT.DEFAULT if no R2L languages
+ * are installed.
+ */
+static int getKeyboardLanguageDirection() {
+ int language = BidiUtil.getKeyboardLanguage();
+ if (language == BidiUtil.KEYBOARD_BIDI) {
+ return SWT.RIGHT;
+ }
+ if (BidiUtil.isKeyboardBidi()) {
+ return SWT.LEFT;
+ }
+ return SWT.DEFAULT;
+}
+/**
+ * Returns whether the current platform supports a bidi language.
+ * <p>
+ *
+ * @return true=bidi is supported, false otherwise.
+ */
+static boolean isBidiPlatform() {
+ return BidiUtil.isBidiPlatform();
+}
+/**
+ * Returns whether the font set in the specified gc supports
+ * character shaping.
+ * <p>
+ *
+ * @param gc the GC that should be tested for character shaping.
+ * @return
+ * true=the font set in the specified gc supports character shaped glyphs
+ * false=the font set in the specified gc doesn't support character shaped glyphs
+ */
+static boolean isCharacterShaped(GC gc) {
+ return (BidiUtil.getFontBidiAttributes(gc) & BidiUtil.GLYPHSHAPE) != 0;
+}
+/**
+ * Returns whether the font set in the specified gc contains
+ * ligatured glyphs.
+ * <p>
+ *
+ * @param gc the GC that should be tested for ligatures.
+ * @return
+ * true=the font set in the specified gc contains ligatured glyphs.
+ * false=the font set in the specified gc doesn't contain ligatured
+ * glyphs.
+ */
+static boolean isLigated(GC gc) {
+ return (BidiUtil.getFontBidiAttributes(gc) & BidiUtil.LIGATE) != 0;
+}
+/**
+ * Removes the keyboard language listener for the specified window.
+ * <p>
+ *
+ * @param control window to remove the keyboard language listener from.
+ */
+static void removeLanguageListener(Control control) {
+ BidiUtil.removeLanguageListener(control.handle);
+}
+/**
+ * Calculates render positions using the glyph distance values in the dx array.
+ */
+private void calculateRenderPositions() {
+ renderPositions = new int[dx.length];
+ renderPositions[0] = StyledText.XINSET;
+ for (int i = 0; i < dx.length - 1; i++) {
+ renderPositions[i + 1] = renderPositions[i] + dx[i];
+ }
+}
+/**
+ * Calculate the line's tab stops and adjust the dx array to
+ * reflect the width of tab characters.
+ * <p>
+ *
+ * @param text the original line text (not reordered) containing
+ * tab characters.
+ * @param tabWidth number of pixels that one tab character represents
+ */
+private void calculateTabStops(String text, int tabWidth) {
+ int tabIndex = text.indexOf('\t', 0);
+ int logicalIndex = 0;
+ int x = 0;
+ int spaceWidth = gc.stringExtent(" ").x;
+
+ while (tabIndex != -1) {
+ for (; logicalIndex < tabIndex; logicalIndex++) {
+ x += dx[order[logicalIndex]];
+ }
+ int tabStop = x + tabWidth;
+ // make sure tab stop is at least one space width apart
+ // from the last character. fixes 4844.
+ if (tabWidth - tabStop % tabWidth < spaceWidth) {
+ tabStop += tabWidth;
+ }
+ tabStop -= tabStop % tabWidth;
+ dx[order[tabIndex]] = tabStop - x;
+ tabIndex = text.indexOf('\t', tabIndex + 1);
+ }
+}
+/**
+ * Renders the specified text segment. All text is rendered L2R
+ * regardless of the direction of the text. The rendered text may
+ * be visually discontiguous if the text segment is bidirectional.
+ * <p>
+ *
+ * @param logicalStart start offset in the logical text
+ * @param length number of logical characters to render
+ * @param xOffset x location of the line start
+ * @param yOffset y location of the line start
+ */
+void drawBidiText(int logicalStart, int length, int xOffset, int yOffset) {
+ Enumeration directionRuns;
+ int endOffset = logicalStart + length;
+
+ if (logicalStart < 0 || endOffset > getTextLength()) {
+ return;
+ }
+ directionRuns = getDirectionRuns(logicalStart, length).elements();
+ while (directionRuns.hasMoreElements()) {
+ DirectionRun run = (DirectionRun) directionRuns.nextElement();
+ int visualStart = run.getVisualStart();
+ int visualEnd = run.getVisualEnd();
+ int x = xOffset + run.getRenderStartX();
+ drawGlyphs(visualStart, visualEnd - visualStart + 1, x, yOffset);
+ }
+}
+/**
+ * Renders a segment of glyphs. Glyphs are visual objects so the
+ * start and length are visual as well. Glyphs are always rendered L2R.
+ * <p>
+ *
+ * @param visualStart start offset of the glyphs to render relative to the
+ * line start.
+ * @param length number of glyphs to render
+ * @param x x location to render at
+ * @param y y location to render at
+ */
+private void drawGlyphs(int visualStart, int length, int x, int y) {
+ char[] renderBuffer = new char[length];
+ int[] renderDx = new int[length];
+ if (length == 0) {
+ return;
+ }
+ System.arraycopy(glyphBuffer, visualStart, renderBuffer, 0, length);
+ // copy the distance values for the desired rendering range
+ System.arraycopy(dx, visualStart, renderDx, 0, length);
+ BidiUtil.drawGlyphs(gc, renderBuffer, renderDx, x, y);
+}
+/**
+ * Fills a rectangle spanning the given logical range.
+ * The rectangle may be visually discontiguous if the text segment
+ * is bidirectional.
+ * <p>
+ *
+ * @param logicalStart logcial start offset of the rectangle
+ * @param length number of logical characters the rectangle should span
+ * @param xOffset x location of the line start
+ * @param yOffset y location of the line start
+ * @param height height of the rectangle
+ */
+void fillBackground(int logicalStart, int length, int xOffset, int yOffset, int height) {
+ Enumeration directionRuns = getDirectionRuns(logicalStart, length).elements();
+
+ if (logicalStart < 0 || logicalStart + length > getTextLength()) {
+ return;
+ }
+ while (directionRuns.hasMoreElements()) {
+ DirectionRun run = (DirectionRun) directionRuns.nextElement();
+ int startX = run.getRenderStartX();
+ gc.fillRectangle(xOffset + startX, yOffset, run.getRenderStopX() - startX, height);
+ }
+}
+/**
+ * Returns the offset and direction that will be used to position the caret for
+ * the given x location. The caret will be placed in front of or behind the
+ * character at location x depending on what type of character (i.e., R2L or L2R)
+ * is at location x. This method is used for positioning the caret when a mouse
+ * click occurs within the widget.
+ * <p>
+ *
+ * @param x the x location of the character in the line.
+ * @return array containing the caret offset and direction for the x location.
+ * index 0: offset relative to the start of the line
+ * index 1: direction, either ST.COLUMN_NEXT or ST.COLUMN_PREVIOUS.
+ * The direction is used to control the caret position at direction
+ * boundaries. The semantics follow the behavior for keyboard cursor
+ * navigation.
+ * Example: RRRLLL
+ * Pressing cursor left (COLUMN_PREVIOUS) in the L2R segment places the cursor
+ * in front of the first character of the L2R segment. Pressing cursor right
+ * (COLUMN_NEXT) in a R2L segment places the cursor behind the last character
+ * of the R2L segment. However, both are the same logical offset.
+ */
+int[] getCaretOffsetAndDirectionAtX(int x) {
+ int lineLength = getTextLength();
+ int offset;
+ int direction;
+
+ if (lineLength == 0) {
+ return new int[] {0, 0};
+ }
+ int eol = renderPositions[renderPositions.length - 1] + dx[dx.length - 1];
+ if (x >= eol) {
+ return new int[] {lineLength, ST.COLUMN_NEXT};
+ }
+ // get the visual offset of the clicked character
+ int visualOffset = getVisualOffsetAtX(x);
+ // figure out if the character was clicked on the right or left
+ int halfway = renderPositions[visualOffset] + dx[visualOffset] / 2;
+ boolean visualLeft = (x <= halfway);
+ offset = getLogicalOffset(visualOffset);
+
+ if (isRightToLeft(offset)) {
+ if (visualLeft) {
+ if (isLigated(gc)) {
+ // the caret should be positioned after the last
+ // character of the ligature
+ offset = getLigatureEndOffset(offset);
+ }
+ offset++;
+ // position the caret as if the caret is to the right
+ // of the character at location x and the NEXT key is
+ // pressed
+ direction = ST.COLUMN_NEXT;
+ }
+ else {
+ // position the caret as if the caret is to the left
+ // of the character at location x and the PREVIOUS key is
+ // pressed
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ }
+ else {
+ if (visualLeft) {
+ // position the caret as if the caret is to the right
+ // of the character at location x and the PREVIOUS key is
+ // pressed
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ else {
+ // position the caret as if the caret is to the left
+ // of the character at location x and the NEXT key is
+ // pressed
+ offset++;
+ direction = ST.COLUMN_NEXT;
+ }
+ }
+ return new int[] {offset, direction};
+}
+/**
+ * Returns the direction segments that are in the specified text
+ * range. The text range may be visually discontiguous if the
+ * text is bidirectional. Each returned direction run has a single
+ * direction and the runs all go from left to right, regardless of
+ * the direction of the text in the segment. User specified segments
+ * (via BidiSegmentListener) are taken into account and result in
+ * separate direction runs.
+ * <p>
+ *
+ * @param logicalStart offset of the logcial start of the first
+ * direction segment
+ * @param length length of the text included in the direction
+ * segments
+ * @return the direction segments that are in the specified
+ * text range, each segment has a single direction.
+ */
+private Vector getDirectionRuns(int logicalStart, int length) {
+ Vector directionRuns = new Vector();
+ int logicalEnd = logicalStart + length - 1;
+ int segmentLogicalStart = logicalStart;
+ int segmentLogicalEnd = segmentLogicalStart;
+
+ if (logicalEnd < getTextLength()) {
+ int bidiSegmentIndex = 0;
+ int bidiSegmentEnd = bidiSegments[bidiSegmentIndex + 1];
+
+ // Find the bidi segment that the direction runs start in.
+ // There will always be at least on bidi segment (for the entire line).
+ while (bidiSegmentIndex < bidiSegments.length - 2 && bidiSegmentEnd <= logicalStart) {
+ bidiSegmentIndex++;
+ bidiSegmentEnd = bidiSegments[bidiSegmentIndex + 1];
+ }
+ while (segmentLogicalEnd <= logicalEnd) {
+ boolean isRightToLeftSegment = isRightToLeft(segmentLogicalStart);
+ // Search for the end of the direction segment. Each segment needs to
+ // be rendered separately.
+ // E.g., 11211 (1=R2L, 2=L2R), rendering from logical index 0 to 5
+ // would be visual 1 to 4 and would thus miss visual 0. Rendering the
+ // segments separately would render from visual 1 to 0, then 2, then
+ // 4 to 3.
+ while (segmentLogicalEnd < logicalEnd &&
+ // If our segment type is RtoL, the order index for the next character should be one less, if there
+ // is no direction change.
+ // If our segment type is LtoR, the order index for the next character will be one more if there is
+ // no direction change.
+ ((isRightToLeftSegment && (order[segmentLogicalEnd + 1]+ 1 == order[segmentLogicalEnd])) ||
+ (isRightToLeftSegment == false && (order[segmentLogicalEnd + 1]- 1 == order[segmentLogicalEnd]))) &&
+ segmentLogicalEnd + 1 < bidiSegmentEnd) {
+ segmentLogicalEnd++;
+ }
+ directionRuns.addElement(new DirectionRun(segmentLogicalStart, segmentLogicalEnd));
+ segmentLogicalStart = ++segmentLogicalEnd;
+ // The current direction run ends at a bidi segment end. Get the next bidi segment.
+ if (segmentLogicalEnd == bidiSegmentEnd && bidiSegmentIndex < bidiSegments.length - 2) {
+ bidiSegmentIndex++;
+ bidiSegmentEnd = bidiSegments[bidiSegmentIndex + 1];
+ }
+ }
+ }
+ return directionRuns;
+}
+/**
+ * Returns the offset of the last character comprising a ligature.
+ * <p>
+ *
+ * @param offset the logical offset of a character that may be a
+ * ligature.
+ * @return the offset of the last character comprising a ligature.
+ */
+int getLigatureEndOffset(int offset) {
+ int newOffset = offset;
+ int i = offset + 1;
+
+ // assume only bidi languages support ligatures
+ if (offset < 0 || offset >= order.length || isRightToLeft(offset) == false) {
+ return offset;
+ }
+ // a ligature is a visual character that is comprised of
+ // multiple logical characters, thus each logical part of
+ // a ligature will have the same order value
+ while (i < order.length && (order[i] == order[offset])) {
+ newOffset = i;
+ i++;
+ }
+ return newOffset;
+}
+/**
+ * Returns the offset of the first character comprising a ligature.
+ * <p>
+ *
+ * @param offset the logical offset of a character that may be a
+ * ligature.
+ * @return the offset of the first character comprising a ligature.
+ */
+int getLigatureStartOffset(int offset) {
+ int newOffset = offset;
+ int i = offset - 1;
+
+ // assume only bidi languages support ligatures
+ if (offset < 0 || offset >= order.length || isRightToLeft(offset) == false) {
+ return offset;
+ }
+ // a ligature is a visual character that is comprised of
+ // multiple logical characters, thus each logical part of
+ // a ligature will have the same order value
+ while (i >= 0 && (order[i] == order[offset])) {
+ newOffset = i;
+ i--;
+ }
+ return newOffset;
+}
+/**
+ * Returns the logical offset of the character at the specified
+ * visual offset.
+ * <p>
+ *
+ * @param visualOffset the visual offset
+ * @return the logical offset of the character at <code>visualOffset</code>.
+ */
+private int getLogicalOffset(int visualOffset) {
+ int logicalOffset = 0;
+
+ while (logicalOffset < order.length && order[logicalOffset] != visualOffset) {
+ logicalOffset++;
+ }
+ return logicalOffset;
+}
+/**
+ * Returns the offset of the character at the specified x location.
+ * <p>
+ *
+ * @param x the location of the character
+ * @return the logical offset of the character at the specified x
+ * location.
+ */
+int getOffsetAtX(int x) {
+ int visualOffset;
+
+ if (getTextLength() == 0) {
+ return 0;
+ }
+ if (x >= renderPositions[renderPositions.length - 1] + dx[dx.length - 1]) {
+ // Return when x is past the end of the line. Fixes 1GLADBK.
+ return -1;
+ }
+ visualOffset = getVisualOffsetAtX(x);
+ return getLogicalOffset(visualOffset);
+}
+/**
+ * Returns the reordering indices that map between logical and
+ * visual index of characters in the specified range.
+ * <p>
+ *
+ * @param start start offset of the reordering indices
+ * @param length number of reordering indices to return
+ * @return the reordering indices that map between logical and
+ * visual index of characters in the specified range. Relative
+ * to the start of the range.
+ */
+private int[] getRenderIndexesFor(int start, int length) {
+ int[] positions = new int[length];
+ int end = start + length;
+
+ for (int i = start; i < end; i++) {
+ positions[i-start] = order[i];
+ }
+ return positions;
+}
+/**
+ * Break up the given ranges such that each range is fully contained within a bidi
+ * segment.
+ */
+private StyleRange[] getSegmentedRangesFor(StyleRange[] ranges) {
+ if ((bidiSegments == null) || (bidiSegments.length == 0)) return ranges;
+ Vector newRanges = new Vector();
+ int j=0;
+ int startSegment;
+ int endSegment;
+ for (int i=0; i<ranges.length; i++) {
+ int start = ranges[i].start;
+ int end = start+ranges[i].length;
+ startSegment=-1;
+ endSegment=-1;
+ boolean done = false;
+ while (j<bidiSegments.length && !done) {
+ if (bidiSegments[j]<=start) {
+ startSegment=j;
+ }
+ if (bidiSegments[j]>=end) {
+ endSegment=j-1;
+ j--;
+ }
+ done = (startSegment != -1) && (endSegment != -1);
+ if (!done) j++;
+ }
+ if (startSegment == endSegment) {
+ // range is within one segment
+ StyleRange newStyle = new StyleRange(start, end-start, null, null);
+ newRanges.addElement(newStyle);
+ } else if (startSegment > endSegment) {
+ // range is within no segment (i.e., it's empty)
+ } else {
+ // range spans multiple segments
+ StyleRange newStyle = new StyleRange(start, bidiSegments[startSegment+1]-start, null, null);
+ newRanges.addElement(newStyle);
+ startSegment++;
+ for (int k=startSegment; k<endSegment; k++) {
+ newStyle = new StyleRange(bidiSegments[k], bidiSegments[k+1]-bidiSegments[k], null, null);
+ newRanges.addElement(newStyle);
+ }
+ newStyle = new StyleRange(bidiSegments[endSegment], end-bidiSegments[endSegment], null, null);
+ newRanges.addElement(newStyle);
+ }
+ }
+ StyleRange[] rangeArray = new StyleRange[newRanges.size()];
+ for (int i=0; i<newRanges.size(); i++) {
+ rangeArray[i]=(StyleRange)newRanges.elementAt(i);
+ }
+ return rangeArray;
+}
+/**
+ * Returns the number of characters in the line.
+ * <p>
+ *
+ * @return the number of characters in the line.
+ */
+private int getTextLength() {
+ return dx.length;
+}
+/**
+ * Returns the x position at the specified offset in the line.
+ * <p>
+ * @param logicalOffset offset of the character in the line.
+ * @return the x position at the specified offset in the line.
+ */
+int getTextPosition(int logicalOffset) {
+ return getTextPosition(logicalOffset, ST.COLUMN_NEXT);
+}
+/**
+ * Returns the x position at the specified offset in the line.
+ * The direction parameter is used to determine the position
+ * at direction boundaries. If the logical offset is between a R2L
+ * and a L2R segment, pressing cursor left in the L2R segment places
+ * the position in front of the first character of the L2R segment; whereas
+ * pressing cursor right in the R2L segment places the position behind
+ * the last character of the R2L segment. However, both x positions
+ * are at the same logical offset.
+ * <p>
+ *
+ * @param logicalOffset offset of the character in the line
+ * @param direction direction the caret moved to the specified location.
+ * either ST.COLUMN_NEXT (right cursor key) or ST.COLUMN_PREVIOUS (left cursor key) .
+ * @return the x position at the specified offset in the line,
+ * taking the direction into account as described above.
+ */
+int getTextPosition(int logicalOffset, int direction) {
+ int caretX;
+
+ if (getTextLength() == 0 || logicalOffset < 0) {
+ return StyledText.XINSET;
+ }
+ // at or past end of line?
+ if (logicalOffset >= order.length) {
+ logicalOffset = Math.min(logicalOffset, order.length - 1);
+ int visualOffset = order[logicalOffset];
+ if (isRightToLeft(logicalOffset)) {
+ caretX = renderPositions[visualOffset];
+ }
+ else {
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ }
+ else
+ // at beginning of line?
+ if (logicalOffset == 0) {
+ int visualOffset = order[logicalOffset];
+ if (isRightToLeft(logicalOffset)) {
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ else {
+ caretX = renderPositions[visualOffset];
+ }
+ }
+ else
+ // always consider local numbers as a direction boundary
+ // because they represent a discontiguous text segment coming from
+ // a R2L segment.
+ // treat user specified direction segments like real direction changes.
+ if (direction == ST.COLUMN_NEXT &&
+ (isRightToLeft(logicalOffset) != isRightToLeft(logicalOffset - 1) ||
+ isLocalNumber(logicalOffset) != isLocalNumber(logicalOffset - 1) ||
+ isStartOfBidiSegment(logicalOffset))) {
+ int visualOffset = order[logicalOffset-1];
+ // moving between segments.
+ // do not consider local numbers as R2L here, to determine position,
+ // because local numbers are navigated L2R and we want the caret to
+ // be to the right of the number. see 1GK9API
+ if (isRightToLeft(logicalOffset - 1)) {
+ // moving from RtoL to LtoR
+ caretX = renderPositions[visualOffset];
+ }
+ else {
+ // moving from LtoR to RtoL
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ }
+ else
+ // consider local numbers as R2L in determining direction boundaries.
+ // fixes 1GK9API.
+ if (direction == ST.COLUMN_PREVIOUS &&
+ isRightToLeftInput(logicalOffset) != isRightToLeftInput(logicalOffset - 1)) {
+ int visualOffset = order[logicalOffset];
+ // moving between segments.
+ // consider local numbers as R2L here, to determine position, because
+ // we want to stay in L2R segment and place the cursor to the left of
+ // first L2R character. see 1GK9API
+ if (isRightToLeftInput(logicalOffset - 1)) {
+ // moving from LtoR to RtoL
+ caretX = renderPositions[visualOffset];
+ }
+ else {
+ // moving from RtoL to LtoR
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ }
+ else
+ if (isRightToLeft(logicalOffset)) {
+ int visualOffset = order[logicalOffset];
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ else {
+ caretX = renderPositions[order[logicalOffset]];
+ }
+ return caretX;
+}
+/**
+ * Returns the width in pixels of the line.
+ * <p>
+ *
+ * @return the width in pixels of the line.
+ */
+int getTextWidth() {
+ int width = 0;
+
+ if (getTextLength() > 0) {
+ width = renderPositions[renderPositions.length - 1] + dx[dx.length - 1];
+ }
+ return width;
+}
+/**
+ * Returns the visual offset of the character at the specified x
+ * location.
+ * <p>
+ *
+ * @param x the location of the character
+ * @return the visual offset of the character at the specified x
+ * location.
+ */
+private int getVisualOffsetAtX(int x) {
+ int lineLength = getTextLength();
+ int low = -1;
+ int high = lineLength;
+
+ while (high - low > 1) {
+ int offset = (high + low) / 2;
+ int visualX = renderPositions[offset];
+
+ // visualX + dx is the start of the next character. Restrict right/high
+ // search boundary only if x is before next character. Fixes 1GL4ZVE.
+ if (x < visualX + dx[offset]) {
+ high = offset;
+ }
+ else
+ if (high == lineLength && high - offset == 1) {
+ // requested x location is past end of line
+ high = -1;
+ }
+ else {
+ low = offset;
+ }
+ }
+ return high;
+}
+/**
+ * Returns if the character at the given offset is a local number.
+ * <p>
+ *
+ * @param logicalIndex the index of the character
+ * @return
+ * true=the character at the specified index is a local number
+ * false=the character at the specified index is not a local number
+ */
+boolean isLocalNumber(int logicalIndex) {
+ boolean isLocalNumber = false;
+
+ if (logicalIndex >= 0 && logicalIndex < classBuffer.length) {
+ isLocalNumber = classBuffer[logicalIndex] == BidiUtil.CLASS_LOCALNUMBER;
+ }
+ return isLocalNumber;
+}
+/**
+ * Returns the direction of the character at the specified index.
+ * Used for rendering and caret positioning where local numbers (e.g.,
+ * national Arabic, or Hindi, numbers) are considered left-to-right.
+ * <p>
+ *
+ * @param logicalIndex the index of the character
+ * @return
+ * true=the character at the specified index is in a right-to-left
+ * codepage (e.g., Hebrew, Arabic).
+ * false=the character at the specified index is in a left-to-right/latin
+ * codepage.
+ */
+boolean isRightToLeft(int logicalIndex) {
+ boolean isRightToLeft = false;
+
+ if (logicalIndex >= 0 && logicalIndex < classBuffer.length) {
+ isRightToLeft = (classBuffer[logicalIndex] == BidiUtil.CLASS_ARABIC) ||
+ (classBuffer[logicalIndex] == BidiUtil.CLASS_HEBREW);
+ }
+ return isRightToLeft;
+}
+/**
+ * Returns the direction of the character at the specified index.
+ * Used for setting the keyboard language where local numbers (e.g.,
+ * national Arabic, or Hindi, numbers) are considered right-to-left.
+ * <p>
+ *
+ * @param logicalIndex the index of the character
+ * @return
+ * true=the character at the specified index is in a right-to-left
+ * codepage (e.g., Hebrew, Arabic).
+ * false=the character at the specified index is in a left-to-right/latin
+ * codepage.
+ */
+boolean isRightToLeftInput(int logicalIndex) {
+ boolean isRightToLeft = false;
+
+ if (logicalIndex >= 0 && logicalIndex < classBuffer.length) {
+ isRightToLeft = (classBuffer[logicalIndex] == BidiUtil.CLASS_ARABIC) ||
+ (classBuffer[logicalIndex] == BidiUtil.CLASS_HEBREW) ||
+ (classBuffer[logicalIndex] == BidiUtil.CLASS_LOCALNUMBER);
+ }
+ return isRightToLeft;
+}
+/**
+ * Returns whether the specified index is the start of a user
+ * specified direction segment.
+ * <p>
+ *
+ * @param logicalIndex the index to test
+ * @return true=the specified index is the start of a user specified
+ * direction segment, false otherwise
+ */
+private boolean isStartOfBidiSegment(int logicalIndex) {
+ for (int i = 0; i < bidiSegments.length; i++) {
+ if (bidiSegments[i] == logicalIndex) return true;
+ }
+ return false;
+}
+/**
+ * Reorders and calculates render positions for the specified sub-line
+ * of text. The results will be merged with the data for the rest of
+ * the line .
+ * <p>
+ *
+ * @param textline the entire line of text that this object represents.
+ * @param logicalStart the start offset of the first character to
+ * reorder.
+ * @param length the number of characters to reorder
+ */
+private void prepareFontStyledText(String textline, int logicalStart, int length) {
+ int byteCount = length;
+ int flags = 0;
+ String text = textline.substring(logicalStart, logicalStart + length);
+
+ // Figure out what is before and after the substring so that the proper character
+ // shaping will occur. Character shaping will not occur across bidi segments, so
+ // if the styled text starts or ends on a bidi segment, do not process the text
+ // for character shaping.
+ if (logicalStart != 0
+ && isCharacterShaped(gc)
+ && !isStartOfBidiSegment(logicalStart)
+ && !Compatibility.isWhitespace(textline.charAt(logicalStart - 1))
+ && isRightToLeft(logicalStart - 1)) {
+ // if the start of the substring is not the beginning of the
+ // text line, check to see what is before the string
+ flags |= BidiUtil.LINKBEFORE;
+ }
+ if ((logicalStart + byteCount) != dx.length
+ && isCharacterShaped(gc)
+ && !isStartOfBidiSegment(logicalStart + length)
+ && !Compatibility.isWhitespace(textline.charAt(logicalStart + byteCount))
+ && isRightToLeft(logicalStart + byteCount)) {
+ // if the end of the substring is not the end of the text line,
+ // check to see what is after the substring
+ flags |= BidiUtil.LINKAFTER;
+ }
+ // set classification values for the substring
+ flags |= BidiUtil.CLASSIN;
+ byte[] classArray = new byte[byteCount];
+ int[] renderIndexes = getRenderIndexesFor(logicalStart, byteCount);
+ for (int i = 0; i < byteCount; i++) {
+ classArray[i] = classBuffer[renderIndexes[i]];
+ }
+ int[] dxArray = new int[byteCount];
+ int[] orderArray = new int[byteCount];
+ BidiUtil.getRenderInfo(gc, text, orderArray, classArray, dxArray, flags, new int[] {0, text.length()});
+ // update the existing dx array with the new dx values based on the bold font
+ for (int i = 0; i < dxArray.length; i++) {
+ int dxValue = dxArray[orderArray[i]];
+ int visualIndex = renderIndexes[i];
+ dx[visualIndex] = dxValue;
+ }
+}
+/**
+ * Redraws a rectangle spanning the given logical range.
+ * The rectangle may be visually discontiguous if the text segment
+ * is bidirectional.
+ * <p>
+ *
+ * @param parent window that should be invalidated
+ * @param logicalStart logcial start offset of the rectangle
+ * @param length number of logical characters the rectangle should span
+ * @param xOffset x location of the line start
+ * @param yOffset y location of the line start
+ * @param height height of the invalidated rectangle
+ */
+void redrawRange(Control parent, int logicalStart, int length, int xOffset, int yOffset, int height) {
+ Enumeration directionRuns;
+
+ if (logicalStart < 0 || logicalStart + length > getTextLength()) {
+ return;
+ }
+ directionRuns = getDirectionRuns(logicalStart, length).elements();
+ while (directionRuns.hasMoreElements()) {
+ DirectionRun run = (DirectionRun) directionRuns.nextElement();
+ int startX = run.getRenderStartX();
+
+ parent.redraw(xOffset + startX, yOffset, run.getRenderStopX() - startX, height, true);
+ }
+}
+/**
+ * Sets the keyboard language to match the codepage of the character
+ * at the specified offset.
+ * Only distinguishes between left-to-right and right-to-left characters and
+ * sets the keyboard language to a bidi or non-bidi language.
+ * <p>
+ *
+ * @param logicalIndex logical offset of the character to use for
+ * determining the new keyboard language.
+ */
+void setKeyboardLanguage(int logicalIndex) {
+ int language;
+ int current = BidiUtil.getKeyboardLanguage();
+
+ if (logicalIndex < 0 || logicalIndex >= classBuffer.length) {
+ return;
+ }
+ if (isRightToLeftInput(logicalIndex)) {
+ // keyboard already in bidi mode, since we cannot distinguish between
+ // multiple bidi languages, just return
+ if (current == BidiUtil.KEYBOARD_BIDI) return;
+ language = BidiUtil.KEYBOARD_BIDI;
+ } else {
+ // keyboard already in non-bidi mode, since we cannot distinguish between
+ // multiple non-bidi languages, just return
+ if (current == BidiUtil.KEYBOARD_NON_BIDI) return;
+ language = BidiUtil.KEYBOARD_NON_BIDI;
+ }
+ BidiUtil.setKeyboardLanguage(language);
+}
+/**
+ * Returns a string representation of the receiver.
+ * <p>
+ *
+ * @return a string representation of the receiver for
+ * debugging purposes. The output order of the StyledTextbidi values
+ * is as follows: order, render position, dx, character class, glyphs.
+ */
+public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("StyledTextBidi {{");
+ // order
+ for (int i = 0; i < order.length; i++) {
+ if (i != 0) {
+ buf.append(",");
+ }
+ buf.append(order[i]);
+ }
+ buf.append("}, {");
+ // render positions
+ for (int i = 0; i < renderPositions.length; i++) {
+ if (i != 0) {
+ buf.append(",");
+ }
+ buf.append(renderPositions[i]);
+ }
+ buf.append("}, {");
+ // dx
+ for (int i = 0; i < dx.length; i++) {
+ if (i != 0) {
+ buf.append(",");
+ }
+ buf.append(dx[i]);
+ }
+ buf.append("}, {");
+ // character class
+ for (int i = 0; i < classBuffer.length; i++) {
+ if (i != 0) {
+ buf.append(",");
+ }
+ buf.append(classBuffer[i]);
+ }
+ buf.append("}, {");
+ // glyphs
+ buf.append(glyphBuffer);
+ buf.append("}}");
+ return buf.toString();
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextContent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextContent.java
index bb46fb4eb4..4a39db49b7 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextContent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextContent.java
@@ -1,202 +1,202 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-/**
- * Clients may implement the StyledTextContent interface to provide a
- * custom store for the StyledText widget content. The StyledText widget
- * interacts with its StyledTextContent in order to access and update
- * the text that is being displayed and edited in the widget.
- * A custom content implementation can be set in the widget using the
- * StyledText.setContent API.
- */
-public interface StyledTextContent {
-
-/**
- * Called by StyledText to add itself as an Observer to content changes.
- * See TextChangeListener for a description of the listener methods that
- * are called when text changes occur.
- * <p>
- *
- * @param listener the listener
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void addTextChangeListener(TextChangeListener listener);
-
-/**
- * Return the number of characters in the content.
- * <p>
- *
- * @return the number of characters in the content.
- */
-public int getCharCount();
-
-/**
- * Return the line at the given line index without delimiters.
- * <p>
- *
- * @param lineIndex index of the line to return. Does not include
- * delimiters of preceeding lines. Index 0 is the first line of the
- * content.
- * @return the line text without delimiters
- */
-public String getLine(int lineIndex);
-
-/**
- * Return the line index at the given character offset.
- * <p>
- *
- * @param offset offset of the line to return. The first character of the
- * document is at offset 0. An offset of getLength() is valid and should
- * answer the number of lines.
- * @return the line index. The first line is at index 0. If the character
- * at offset is a delimiter character, answer the line index of the line
- * that is delimited.
- * For example, if text = "\r\n\r\n", and delimiter = "\r\n", then:
- * <ul>
- * <li>getLineAtOffset(0) == 0
- * <li>getLineAtOffset(1) == 0
- * <li>getLineAtOffset(2) == 1
- * <li>getLineAtOffset(3) == 1
- * <li>getLineAtOffset(4) == 2
- * </ul>
- */
-public int getLineAtOffset(int offset);
-
-/**
- * Return the number of lines. Should answer 1 when no text is specified.
- * The StyledText widget relies on this behavior for drawing the cursor.
- * <p>
- *
- * @return the number of lines. For example:
- * <ul>
- * <li> text value ==> getLineCount
- * <li> null ==> 1
- * <li> "" ==> 1
- * <li> "a\n" ==> 2
- * <li> "\n\n" ==> 3
- * </ul>
- */
-public int getLineCount();
-
-/**
- * Return the line delimiter that should be used by the StyledText
- * widget when inserting new lines. New lines entered using key strokes
- * and paste operations use this line delimiter.
- * Implementors may use System.getProperty("line.separator") to return
- * the platform line delimiter.
- * <p>
- *
- * @return the line delimiter that should be used by the StyledText widget
- * when inserting new lines.
- */
-public String getLineDelimiter();
-
-/**
- * Return the character offset of the first character of the given line.
- * <p>
- * <b>NOTE:</b> When there is no text (i.e., no lines), getOffsetAtLine(0)
- * is a valid call that should return 0.
- * </p>
- *
- * @param lineIndex index of the line. The first line is at index 0.
- * @return offset offset of the first character of the line. The first
- * character of the document is at offset 0. The return value should
- * include line delimiters.
- * For example, if text = "\r\ntest\r\n" and delimiter = "\r\n", then:
- * <ul>
- * <li>getOffsetAtLine(0) == 0
- * <li>getOffsetAtLine(1) == 2
- * <li>getOffsetAtLine(2) == 8
- * </ul>
- */
-public int getOffsetAtLine(int lineIndex);
-
-/**
- * Returns a string representing the content at the given range.
- * <p>
- *
- * @param start the start offset of the text to return. Offset 0 is the
- * first character of the document.
- * @param length the length of the text to return
- * @return the text at the given range
- */
-public String getTextRange(int start, int length);
-
-/**
- * Remove the specified text changed listener.
- * <p>
- *
- * @param listener the listener
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT when listener is null</li>
- * </ul>
- */
-public void removeTextChangeListener(TextChangeListener listener);
-
-/**
- * Replace the text with "newText" starting at position "start"
- * for a length of "replaceLength".
- * <p>
- * Implementors have to notify the TextChangeListeners that were added
- * using <code>addTextChangeListener</code> before and after the content
- * is changed. A <code>TextChangingEvent</code> has to be sent to the
- * textChanging method before the content is changed and a
- * <code>TextChangedEvent</code> has to be sent to the textChanged method
- * after the content has changed.
- * The text change that occurs after the <code>TextChangingEvent</code>
- * has been sent has to be consistent with the data provided in the
- * <code>TextChangingEvent</code>.
- * This data will be cached by the widget and will be used when the
- * <code>TextChangedEvent</code> is received.
- * <p>
- * The <code>TextChangingEvent</code> should be set as follows:
- * <ul>
- * <li>event.start = start of the replaced text
- * <li>event.newText = text that is going to be inserted or empty String
- * if no text will be inserted
- * <li>event.replaceCharCount = length of text that is going to be replaced
- * <li>event.newCharCount = length of text that is going to be inserted
- * <li>event.replaceLineCount = number of lines that are going to be replaced
- * <li>event.newLineCount = number of new lines that are going to be inserted
- * </ul>
- * <b>NOTE:</b> newLineCount is the number of inserted lines and replaceLineCount
- * is the number of deleted lines based on the change that occurs visually.
- * For example:
- * <ul>
- * <li>(replaceText, newText) ==> (replaceLineCount, newLineCount)
- * <li>("", "\n") ==> (0, 1)
- * <li>("\n\n", "a") ==> (2, 0)
- * <li>("a", "\n\n") ==> (0, 2)
- * <li>("\n", "") ==> (1, 0)
- * </ul>
- * </p>
- *
- * @param start start offset of text to replace, none of the offsets include
- * delimiters of preceeding lines, offset 0 is the first character of the
- * document
- * @param replaceLength start offset of text to replace
- * @param newText start offset of text to replace
- * @see TextChangeListener
- */
-public void replaceTextRange(int start, int replaceLength, String text);
-
-/**
- * Set text to "text".
- * Implementors have to send a <code>TextChangedEvent</code> to the
- * textSet method of the TextChangeListeners that were added using
- * <code>addTextChangeListener</code>.
- * <p>
- *
- * @param text the new text
- * @see TextChangeListener
- */
-public void setText(String text);
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+/**
+ * Clients may implement the StyledTextContent interface to provide a
+ * custom store for the StyledText widget content. The StyledText widget
+ * interacts with its StyledTextContent in order to access and update
+ * the text that is being displayed and edited in the widget.
+ * A custom content implementation can be set in the widget using the
+ * StyledText.setContent API.
+ */
+public interface StyledTextContent {
+
+/**
+ * Called by StyledText to add itself as an Observer to content changes.
+ * See TextChangeListener for a description of the listener methods that
+ * are called when text changes occur.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void addTextChangeListener(TextChangeListener listener);
+
+/**
+ * Return the number of characters in the content.
+ * <p>
+ *
+ * @return the number of characters in the content.
+ */
+public int getCharCount();
+
+/**
+ * Return the line at the given line index without delimiters.
+ * <p>
+ *
+ * @param lineIndex index of the line to return. Does not include
+ * delimiters of preceeding lines. Index 0 is the first line of the
+ * content.
+ * @return the line text without delimiters
+ */
+public String getLine(int lineIndex);
+
+/**
+ * Return the line index at the given character offset.
+ * <p>
+ *
+ * @param offset offset of the line to return. The first character of the
+ * document is at offset 0. An offset of getLength() is valid and should
+ * answer the number of lines.
+ * @return the line index. The first line is at index 0. If the character
+ * at offset is a delimiter character, answer the line index of the line
+ * that is delimited.
+ * For example, if text = "\r\n\r\n", and delimiter = "\r\n", then:
+ * <ul>
+ * <li>getLineAtOffset(0) == 0
+ * <li>getLineAtOffset(1) == 0
+ * <li>getLineAtOffset(2) == 1
+ * <li>getLineAtOffset(3) == 1
+ * <li>getLineAtOffset(4) == 2
+ * </ul>
+ */
+public int getLineAtOffset(int offset);
+
+/**
+ * Return the number of lines. Should answer 1 when no text is specified.
+ * The StyledText widget relies on this behavior for drawing the cursor.
+ * <p>
+ *
+ * @return the number of lines. For example:
+ * <ul>
+ * <li> text value ==> getLineCount
+ * <li> null ==> 1
+ * <li> "" ==> 1
+ * <li> "a\n" ==> 2
+ * <li> "\n\n" ==> 3
+ * </ul>
+ */
+public int getLineCount();
+
+/**
+ * Return the line delimiter that should be used by the StyledText
+ * widget when inserting new lines. New lines entered using key strokes
+ * and paste operations use this line delimiter.
+ * Implementors may use System.getProperty("line.separator") to return
+ * the platform line delimiter.
+ * <p>
+ *
+ * @return the line delimiter that should be used by the StyledText widget
+ * when inserting new lines.
+ */
+public String getLineDelimiter();
+
+/**
+ * Return the character offset of the first character of the given line.
+ * <p>
+ * <b>NOTE:</b> When there is no text (i.e., no lines), getOffsetAtLine(0)
+ * is a valid call that should return 0.
+ * </p>
+ *
+ * @param lineIndex index of the line. The first line is at index 0.
+ * @return offset offset of the first character of the line. The first
+ * character of the document is at offset 0. The return value should
+ * include line delimiters.
+ * For example, if text = "\r\ntest\r\n" and delimiter = "\r\n", then:
+ * <ul>
+ * <li>getOffsetAtLine(0) == 0
+ * <li>getOffsetAtLine(1) == 2
+ * <li>getOffsetAtLine(2) == 8
+ * </ul>
+ */
+public int getOffsetAtLine(int lineIndex);
+
+/**
+ * Returns a string representing the content at the given range.
+ * <p>
+ *
+ * @param start the start offset of the text to return. Offset 0 is the
+ * first character of the document.
+ * @param length the length of the text to return
+ * @return the text at the given range
+ */
+public String getTextRange(int start, int length);
+
+/**
+ * Remove the specified text changed listener.
+ * <p>
+ *
+ * @param listener the listener
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT when listener is null</li>
+ * </ul>
+ */
+public void removeTextChangeListener(TextChangeListener listener);
+
+/**
+ * Replace the text with "newText" starting at position "start"
+ * for a length of "replaceLength".
+ * <p>
+ * Implementors have to notify the TextChangeListeners that were added
+ * using <code>addTextChangeListener</code> before and after the content
+ * is changed. A <code>TextChangingEvent</code> has to be sent to the
+ * textChanging method before the content is changed and a
+ * <code>TextChangedEvent</code> has to be sent to the textChanged method
+ * after the content has changed.
+ * The text change that occurs after the <code>TextChangingEvent</code>
+ * has been sent has to be consistent with the data provided in the
+ * <code>TextChangingEvent</code>.
+ * This data will be cached by the widget and will be used when the
+ * <code>TextChangedEvent</code> is received.
+ * <p>
+ * The <code>TextChangingEvent</code> should be set as follows:
+ * <ul>
+ * <li>event.start = start of the replaced text
+ * <li>event.newText = text that is going to be inserted or empty String
+ * if no text will be inserted
+ * <li>event.replaceCharCount = length of text that is going to be replaced
+ * <li>event.newCharCount = length of text that is going to be inserted
+ * <li>event.replaceLineCount = number of lines that are going to be replaced
+ * <li>event.newLineCount = number of new lines that are going to be inserted
+ * </ul>
+ * <b>NOTE:</b> newLineCount is the number of inserted lines and replaceLineCount
+ * is the number of deleted lines based on the change that occurs visually.
+ * For example:
+ * <ul>
+ * <li>(replaceText, newText) ==> (replaceLineCount, newLineCount)
+ * <li>("", "\n") ==> (0, 1)
+ * <li>("\n\n", "a") ==> (2, 0)
+ * <li>("a", "\n\n") ==> (0, 2)
+ * <li>("\n", "") ==> (1, 0)
+ * </ul>
+ * </p>
+ *
+ * @param start start offset of text to replace, none of the offsets include
+ * delimiters of preceeding lines, offset 0 is the first character of the
+ * document
+ * @param replaceLength start offset of text to replace
+ * @param newText start offset of text to replace
+ * @see TextChangeListener
+ */
+public void replaceTextRange(int start, int replaceLength, String text);
+
+/**
+ * Set text to "text".
+ * Implementors have to send a <code>TextChangedEvent</code> to the
+ * textSet method of the TextChangeListeners that were added using
+ * <code>addTextChangeListener</code>.
+ * <p>
+ *
+ * @param text the new text
+ * @see TextChangeListener
+ */
+public void setText(String text);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextEvent.java
index 8011bbfda8..8e4b598347 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextEvent.java
@@ -1,34 +1,34 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- *
- */
-class StyledTextEvent extends Event {
- // used by LineStyleEvent
- StyleRange[] styles;
- // used by LineBackgroundEvent
- Color lineBackground;
- // used by BidiSegmentEvent
- int[] segments;
- // used by TextChangedEvent
- int replaceCharCount;
- int newCharCount;
- int replaceLineCount;
- int newLineCount;
-
-StyledTextEvent (StyledTextContent content) {
- super();
- data = content;
-}
-}
-
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ *
+ */
+class StyledTextEvent extends Event {
+ // used by LineStyleEvent
+ StyleRange[] styles;
+ // used by LineBackgroundEvent
+ Color lineBackground;
+ // used by BidiSegmentEvent
+ int[] segments;
+ // used by TextChangedEvent
+ int replaceCharCount;
+ int newCharCount;
+ int replaceLineCount;
+ int newLineCount;
+
+StyledTextEvent (StyledTextContent content) {
+ super();
+ data = content;
+}
+}
+
+
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextListener.java
index 5f156af93a..825f546992 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextListener.java
@@ -1,73 +1,73 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.internal.SWTEventListener;
-
-class StyledTextListener extends TypedListener {
-/**
- */
-StyledTextListener(SWTEventListener listener) {
- super(listener);
-}
-/**
- * Process StyledText events by invoking the event's handler.
- */
-public void handleEvent(Event e) {
- TextChangedEvent textChangedEvent;
-
- switch (e.type) {
- case StyledText.ExtendedModify:
- ExtendedModifyEvent extendedModifyEvent = new ExtendedModifyEvent((StyledTextEvent) e);
- ((ExtendedModifyListener) eventListener).modifyText(extendedModifyEvent);
- break;
-
- case StyledText.LineGetBackground:
- LineBackgroundEvent lineBgEvent = new LineBackgroundEvent((StyledTextEvent) e);
- ((LineBackgroundListener) eventListener).lineGetBackground(lineBgEvent);
- ((StyledTextEvent) e).lineBackground = lineBgEvent.lineBackground;
- break;
-
- case StyledText.LineGetSegments:
- BidiSegmentEvent segmentEvent = new BidiSegmentEvent((StyledTextEvent) e);
- ((BidiSegmentListener) eventListener).lineGetSegments(segmentEvent);
- ((StyledTextEvent) e).segments = segmentEvent.segments;
- break;
-
- case StyledText.LineGetStyle:
- LineStyleEvent lineStyleEvent = new LineStyleEvent((StyledTextEvent) e);
- ((LineStyleListener) eventListener).lineGetStyle(lineStyleEvent);
- ((StyledTextEvent) e).styles = lineStyleEvent.styles;
- break;
-
- case StyledText.VerifyKey:
- VerifyEvent verifyEvent = new VerifyEvent(e);
- ((VerifyKeyListener) eventListener).verifyKey(verifyEvent);
- e.doit = verifyEvent.doit;
- break;
-
- case StyledText.TextChanged:
- textChangedEvent = new TextChangedEvent((StyledTextContent) e.data);
- ((TextChangeListener) eventListener).textChanged(textChangedEvent);
- break;
-
- case StyledText.TextChanging:
- TextChangingEvent textChangingEvent = new TextChangingEvent((StyledTextContent) e.data, (StyledTextEvent) e);
- ((TextChangeListener) eventListener).textChanging(textChangingEvent);
- break;
-
- case StyledText.TextSet:
- textChangedEvent = new TextChangedEvent((StyledTextContent) e.data);
- ((TextChangeListener) eventListener).textSet(textChangedEvent);
- break;
- }
-}
-}
-
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.internal.SWTEventListener;
+
+class StyledTextListener extends TypedListener {
+/**
+ */
+StyledTextListener(SWTEventListener listener) {
+ super(listener);
+}
+/**
+ * Process StyledText events by invoking the event's handler.
+ */
+public void handleEvent(Event e) {
+ TextChangedEvent textChangedEvent;
+
+ switch (e.type) {
+ case StyledText.ExtendedModify:
+ ExtendedModifyEvent extendedModifyEvent = new ExtendedModifyEvent((StyledTextEvent) e);
+ ((ExtendedModifyListener) eventListener).modifyText(extendedModifyEvent);
+ break;
+
+ case StyledText.LineGetBackground:
+ LineBackgroundEvent lineBgEvent = new LineBackgroundEvent((StyledTextEvent) e);
+ ((LineBackgroundListener) eventListener).lineGetBackground(lineBgEvent);
+ ((StyledTextEvent) e).lineBackground = lineBgEvent.lineBackground;
+ break;
+
+ case StyledText.LineGetSegments:
+ BidiSegmentEvent segmentEvent = new BidiSegmentEvent((StyledTextEvent) e);
+ ((BidiSegmentListener) eventListener).lineGetSegments(segmentEvent);
+ ((StyledTextEvent) e).segments = segmentEvent.segments;
+ break;
+
+ case StyledText.LineGetStyle:
+ LineStyleEvent lineStyleEvent = new LineStyleEvent((StyledTextEvent) e);
+ ((LineStyleListener) eventListener).lineGetStyle(lineStyleEvent);
+ ((StyledTextEvent) e).styles = lineStyleEvent.styles;
+ break;
+
+ case StyledText.VerifyKey:
+ VerifyEvent verifyEvent = new VerifyEvent(e);
+ ((VerifyKeyListener) eventListener).verifyKey(verifyEvent);
+ e.doit = verifyEvent.doit;
+ break;
+
+ case StyledText.TextChanged:
+ textChangedEvent = new TextChangedEvent((StyledTextContent) e.data);
+ ((TextChangeListener) eventListener).textChanged(textChangedEvent);
+ break;
+
+ case StyledText.TextChanging:
+ TextChangingEvent textChangingEvent = new TextChangingEvent((StyledTextContent) e.data, (StyledTextEvent) e);
+ ((TextChangeListener) eventListener).textChanging(textChangingEvent);
+ break;
+
+ case StyledText.TextSet:
+ textChangedEvent = new TextChangedEvent((StyledTextContent) e.data);
+ ((TextChangeListener) eventListener).textSet(textChangedEvent);
+ break;
+ }
+}
+}
+
+
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableCursor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableCursor.java
index 8cd05e8a1f..6727675f61 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableCursor.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableCursor.java
@@ -1,515 +1,515 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.events.*;
-
-/**
- * A TableCursor provides a way for the user to navigate around a Table
- * using the keyboard. It also provides a mechanism for selecting an
- * individual cell in a table.
- *
- * <p> Here is an example of using a TableCursor to navigate to a cell and then edit it.
- *
- * <code><pre>
- * public static void main(String[] args) {
- * Display display = new Display();
- * Shell shell = new Shell(display);
- * shell.setLayout(new GridLayout());
- *
- * // create a a table with 3 columns and fill with data
- * final Table table = new Table(shell, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
- * table.setLayoutData(new GridData(GridData.FILL_BOTH));
- * TableColumn column1 = new TableColumn(table, SWT.NONE);
- * TableColumn column2 = new TableColumn(table, SWT.NONE);
- * TableColumn column3 = new TableColumn(table, SWT.NONE);
- * for (int i = 0; i < 100; i++) {
- * TableItem item = new TableItem(table, SWT.NONE);
- * item.setText(new String[] { "cell "+i+" 0", "cell "+i+" 1", "cell "+i+" 2"});
- * }
- * column1.pack();
- * column2.pack();
- * column3.pack();
- *
- * // create a TableCursor to navigate around the table
- * final TableCursor cursor = new TableCursor(table, SWT.NONE);
- * // create an editor to edit the cell when the user hits "ENTER"
- * // while over a cell in the table
- * final ControlEditor editor = new ControlEditor(cursor);
- * editor.grabHorizontal = true;
- * editor.grabVertical = true;
- *
- * cursor.addSelectionListener(new SelectionAdapter() {
- * // when the TableEditor is over a cell, select the corresponding row in
- * // the table
- * public void widgetSelected(SelectionEvent e) {
- * table.setSelection(new TableItem[] {cursor.getRow()});
- * }
- * // when the user hits "ENTER" in the TableCursor, pop up a text editor so that
- * // they can change the text of the cell
- * public void widgetDefaultSelected(SelectionEvent e){
- * final Text text = new Text(cursor, SWT.NONE);
- * TableItem row = cursor.getRow();
- * int column = cursor.getColumn();
- * text.setText(row.getText(column));
- * text.addKeyListener(new KeyAdapter() {
- * public void keyPressed(KeyEvent e) {
- * // close the text editor and copy the data over
- * // when the user hits "ENTER"
- * if (e.character == SWT.CR) {
- * TableItem row = cursor.getRow();
- * int column = cursor.getColumn();
- * row.setText(column, text.getText());
- * text.dispose();
- * }
- * // close the text editor when the user hits "ESC"
- * if (e.character == SWT.ESC) {
- * text.dispose();
- * }
- * }
- * });
- * editor.setEditor(text);
- * text.setFocus();
- * }
- * });
- * // Hide the TableCursor when the user hits the "CTRL" or "SHIFT" key.
- * // This alows the user to select multiple items in the table.
- * cursor.addKeyListener(new KeyAdapter() {
- * public void keyPressed(KeyEvent e) {
- * if (e.keyCode == SWT.CTRL ||
- * e.keyCode == SWT.SHIFT ||
- * (e.stateMask & SWT.CONTROL) != 0 ||
- * (e.stateMask & SWT.SHIFT) != 0) {
- * cursor.setVisible(false);
- * }
- * }
- * });
- * // Show the TableCursor when the user releases the "SHIFT" or "CTRL" key.
- * // This signals the end of the multiple selection task.
- * table.addKeyListener(new KeyAdapter() {
- * public void keyReleased(KeyEvent e) {
- * if (e.keyCode == SWT.CONTROL && (e.stateMask & SWT.SHIFT) != 0) return;
- * if (e.keyCode == SWT.SHIFT && (e.stateMask & SWT.CONTROL) != 0) return;
- * if (e.keyCode != SWT.CONTROL && (e.stateMask & SWT.CONTROL) != 0) return;
- * if (e.keyCode != SWT.SHIFT && (e.stateMask & SWT.SHIFT) != 0) return;
- *
- * TableItem[] selection = table.getSelection();
- * TableItem row = (selection.length == 0) ? table.getItem(table.getTopIndex()) : selection[0];
- * table.showItem(row);
- * cursor.setSelection(row, 0);
- * cursor.setVisible(true);
- * cursor.setFocus();
- * }
- * });
- *
- * shell.open();
- * while (!shell.isDisposed()) {
- * if (!display.readAndDispatch())
- * display.sleep();
- * }
- * display.dispose();
- * }
- * </pre></code>
- *
- * <dl>
- * <dt><b>Styles:</b></dt>
- * <dd>BORDER</dd>
- * <dt><b>Events:</b></dt>
- * <dd>Selection, DefaultSelection</dd>
- * </dl>
- *
- * @since 2.0
- *
- */
-public class TableCursor extends Canvas {
- Table table;
- int row, column;
- Listener tableListener, resizeListener;
-
-/**
- * Constructs a new instance of this class given its parent
- * table and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a Table control which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
- * </ul>
- *
- * @see SWT#BORDER
- * @see Widget#checkSubclass
- * @see Widget#getStyle
- */
-public TableCursor(Table parent, int style) {
- super(parent, style);
- table = parent;
- Listener listener = new Listener() {
- public void handleEvent(Event event) {
- switch (event.type) {
- case SWT.Dispose :
- dispose(event);
- break;
- case SWT.KeyDown :
- keyDown(event);
- break;
- case SWT.Paint :
- paint(event);
- break;
- case SWT.Traverse :
- traverse(event);
- break;
- }
- }
- };
- addListener(SWT.Dispose, listener);
- addListener(SWT.KeyDown, listener);
- addListener(SWT.Paint, listener);
- addListener(SWT.Traverse, listener);
-
- tableListener = new Listener() {
- public void handleEvent(Event event) {
- switch (event.type) {
- case SWT.MouseDown :
- tableMouseDown(event);
- break;
- case SWT.FocusIn :
- tableFocusIn(event);
- break;
- }
- }
- };
- table.addListener(SWT.FocusIn, tableListener);
- table.addListener(SWT.MouseDown, tableListener);
-
- resizeListener = new Listener() {
- public void handleEvent(Event event) {
- resize();
- }
- };
- int columns = table.getColumnCount();
- for (int i = 0; i < columns; i++) {
- TableColumn column = table.getColumn(i);
- column.addListener(SWT.Resize, resizeListener);
- }
- ScrollBar hBar = table.getHorizontalBar();
- if (hBar != null) {
- hBar.addListener(SWT.Selection, resizeListener);
- }
- ScrollBar vBar = table.getVerticalBar();
- if (vBar != null) {
- vBar.addListener(SWT.Selection, resizeListener);
- }
-}
-
-/**
- * Adds the listener to the collection of listeners who will
- * be notified when the receiver's selection changes, by sending
- * it one of the messages defined in the <code>SelectionListener</code>
- * interface.
- * <p>
- * When <code>widgetSelected</code> is called, the item field of the event object is valid.
- * If the reciever has <code>SWT.CHECK</code> style set and the check selection changes,
- * the event object detail field contains the value <code>SWT.CHECK</code>.
- * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
- * </p>
- *
- * @param listener the listener which should be notified
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @see SelectionListener
- * @see #removeSelectionListener
- * @see SelectionEvent
- */
-public void addSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null)
- SWT.error(SWT.ERROR_NULL_ARGUMENT);
- TypedListener typedListener = new TypedListener(listener);
- addListener(SWT.Selection, typedListener);
- addListener(SWT.DefaultSelection, typedListener);
-}
-
-void dispose(Event event) {
- Display display = getDisplay();
- display.asyncExec(new Runnable() {
- public void run() {
- if (table.isDisposed())
- return;
- table.removeListener(SWT.FocusIn, tableListener);
- table.removeListener(SWT.MouseDown, tableListener);
- int columns = table.getColumnCount();
- for (int i = 0; i < columns; i++) {
- TableColumn column = table.getColumn(i);
- column.removeListener(SWT.Resize, resizeListener);
- }
- ScrollBar hBar = table.getHorizontalBar();
- if (hBar != null) {
- hBar.removeListener(SWT.Selection, resizeListener);
- }
- ScrollBar vBar = table.getVerticalBar();
- if (vBar != null) {
- vBar.removeListener(SWT.Selection, resizeListener);
- }
- }
- });
-}
-
-void keyDown(Event event) {
- switch (event.character) {
- case SWT.CR :
- notifyListeners(SWT.DefaultSelection, new Event());
- return;
- }
- switch (event.keyCode) {
- case SWT.ARROW_UP :
- setRowColumn(row - 1, column, true);
- break;
- case SWT.ARROW_DOWN :
- setRowColumn(row + 1, column, true);
- break;
- case SWT.ARROW_LEFT :
- setRowColumn(row, column - 1, true);
- break;
- case SWT.ARROW_RIGHT :
- setRowColumn(row, column + 1, true);
- break;
- case SWT.HOME :
- setRowColumn(0, column, true);
- break;
- case SWT.END :
- {
- int row = table.getItemCount() - 1;
- setRowColumn(row, column, true);
- break;
- }
- case SWT.PAGE_UP :
- {
- int index = table.getTopIndex();
- if (index == row) {
- Rectangle rect = table.getClientArea();
- TableItem item = table.getItem(index);
- Rectangle itemRect = item.getBounds(0);
- rect.height -= itemRect.y;
- int height = table.getItemHeight();
- int page = Math.max(1, rect.height / height);
- index = Math.max(0, index - page + 1);
- }
- setRowColumn(index, column, true);
- break;
- }
- case SWT.PAGE_DOWN :
- {
- int index = table.getTopIndex();
- Rectangle rect = table.getClientArea();
- TableItem item = table.getItem(index);
- Rectangle itemRect = item.getBounds(0);
- rect.height -= itemRect.y;
- int height = table.getItemHeight();
- int page = Math.max(1, rect.height / height);
- int end = table.getItemCount() - 1;
- index = Math.min(end, index + page - 1);
- if (index == row) {
- index = Math.min(end, index + page - 1);
- }
- setRowColumn(index, column, true);
- break;
- }
- }
-}
-
-void paint(Event event) {
- GC gc = event.gc;
- Display display = getDisplay();
- gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
- gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
- gc.fillRectangle(event.x, event.y, event.width, event.height);
- TableItem item = table.getItem(row);
- int x = 0, y = 0;
- Point size = getSize();
- Image image = item.getImage(column);
- if (image != null) {
- Rectangle imageSize = image.getBounds();
- int imageY = y + (int) (((float) size.y - (float) imageSize.height) / 2.0);
- gc.drawImage(image, x, imageY);
- x += imageSize.width;
- }
- x += (column == 0) ? 2 : 6;
-
- int textY =
- y + (int) (((float) size.y - (float) gc.getFontMetrics().getHeight()) / 2.0);
- gc.drawString(item.getText(column), x, textY);
- if (isFocusControl()) {
- gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
- gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
- gc.drawFocus(0, 0, size.x, size.y);
- }
-
-}
-
-void tableFocusIn(Event event) {
- if (isDisposed())
- return;
- if (isVisible())
- setFocus();
-}
-
-void tableMouseDown(Event event) {
- if (isDisposed() || !isVisible())
- return;
- Point pt = new Point(event.x, event.y);
- Rectangle clientRect = table.getClientArea();
- int columns = table.getColumnCount();
- int start = table.getTopIndex();
- int end = table.getItemCount();
- for (int row = start; row < end; row++) {
- TableItem item = table.getItem(row);
- for (int column = 0; column < columns; column++) {
- Rectangle rect = item.getBounds(column);
- if (rect.y > clientRect.y + clientRect.height)
- return;
- if (rect.contains(pt)) {
- setRowColumn(row, column, true);
- setFocus();
- return;
- }
- }
- }
-}
-
-void traverse(Event event) {
- switch (event.detail) {
- case SWT.TRAVERSE_ARROW_NEXT :
- case SWT.TRAVERSE_ARROW_PREVIOUS :
- case SWT.TRAVERSE_RETURN :
- event.doit = false;
- return;
- }
- event.doit = true;
-}
-
-void setRowColumn(int row, int column, boolean notify) {
- if (0 <= row && row < table.getItemCount()) {
- if (0 <= column && column < table.getColumnCount()) {
- this.row = row;
- this.column = column;
- TableItem item = table.getItem(row);
- table.showItem(item);
- setBounds(item.getBounds(column));
- redraw();
- if (notify) {
- notifyListeners(SWT.Selection, new Event());
- }
- }
- }
-}
-
-public void setVisible(boolean visible) {
- checkWidget();
- if (visible)
- resize();
- super.setVisible(visible);
-}
-
-void resize() {
- TableItem item = table.getItem(row);
- setBounds(item.getBounds(column));
-}
-/**
- * Returns the column over which the TableCursor is positioned.
- *
- * @return the column for the current position
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public int getColumn() {
- checkWidget();
- return column;
-}
-/**
- * Returns the row over which the TableCursor is positioned.
- *
- * @return the item for the current position
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public TableItem getRow() {
- checkWidget();
- return table.getItem(row);
-}
-/**
- * Positions the TableCursor over the cell at the given row and column in the parent table.
- *
- * @param row the index of the row for the cell to select
- * @param column the index of column for the cell to select
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- */
-public void setSelection(int row, int column) {
- checkWidget();
- if (row < 0
- || row >= table.getItemCount()
- || column < 0
- || column >= table.getColumnCount())
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- setRowColumn(row, column, false);
-}
-/**
- * Positions the TableCursor over the cell at the given row and column in the parent table.
- *
- * @param row the TableItem of the row for the cell to select
- * @param column the index of column for the cell to select
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- */
-public void setSelection(TableItem row, int column) {
- checkWidget();
- if (row == null
- || row.isDisposed()
- || column < 0
- || column >= table.getColumnCount())
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- setRowColumn(table.indexOf(row), column, false);
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * A TableCursor provides a way for the user to navigate around a Table
+ * using the keyboard. It also provides a mechanism for selecting an
+ * individual cell in a table.
+ *
+ * <p> Here is an example of using a TableCursor to navigate to a cell and then edit it.
+ *
+ * <code><pre>
+ * public static void main(String[] args) {
+ * Display display = new Display();
+ * Shell shell = new Shell(display);
+ * shell.setLayout(new GridLayout());
+ *
+ * // create a a table with 3 columns and fill with data
+ * final Table table = new Table(shell, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
+ * table.setLayoutData(new GridData(GridData.FILL_BOTH));
+ * TableColumn column1 = new TableColumn(table, SWT.NONE);
+ * TableColumn column2 = new TableColumn(table, SWT.NONE);
+ * TableColumn column3 = new TableColumn(table, SWT.NONE);
+ * for (int i = 0; i < 100; i++) {
+ * TableItem item = new TableItem(table, SWT.NONE);
+ * item.setText(new String[] { "cell "+i+" 0", "cell "+i+" 1", "cell "+i+" 2"});
+ * }
+ * column1.pack();
+ * column2.pack();
+ * column3.pack();
+ *
+ * // create a TableCursor to navigate around the table
+ * final TableCursor cursor = new TableCursor(table, SWT.NONE);
+ * // create an editor to edit the cell when the user hits "ENTER"
+ * // while over a cell in the table
+ * final ControlEditor editor = new ControlEditor(cursor);
+ * editor.grabHorizontal = true;
+ * editor.grabVertical = true;
+ *
+ * cursor.addSelectionListener(new SelectionAdapter() {
+ * // when the TableEditor is over a cell, select the corresponding row in
+ * // the table
+ * public void widgetSelected(SelectionEvent e) {
+ * table.setSelection(new TableItem[] {cursor.getRow()});
+ * }
+ * // when the user hits "ENTER" in the TableCursor, pop up a text editor so that
+ * // they can change the text of the cell
+ * public void widgetDefaultSelected(SelectionEvent e){
+ * final Text text = new Text(cursor, SWT.NONE);
+ * TableItem row = cursor.getRow();
+ * int column = cursor.getColumn();
+ * text.setText(row.getText(column));
+ * text.addKeyListener(new KeyAdapter() {
+ * public void keyPressed(KeyEvent e) {
+ * // close the text editor and copy the data over
+ * // when the user hits "ENTER"
+ * if (e.character == SWT.CR) {
+ * TableItem row = cursor.getRow();
+ * int column = cursor.getColumn();
+ * row.setText(column, text.getText());
+ * text.dispose();
+ * }
+ * // close the text editor when the user hits "ESC"
+ * if (e.character == SWT.ESC) {
+ * text.dispose();
+ * }
+ * }
+ * });
+ * editor.setEditor(text);
+ * text.setFocus();
+ * }
+ * });
+ * // Hide the TableCursor when the user hits the "CTRL" or "SHIFT" key.
+ * // This alows the user to select multiple items in the table.
+ * cursor.addKeyListener(new KeyAdapter() {
+ * public void keyPressed(KeyEvent e) {
+ * if (e.keyCode == SWT.CTRL ||
+ * e.keyCode == SWT.SHIFT ||
+ * (e.stateMask & SWT.CONTROL) != 0 ||
+ * (e.stateMask & SWT.SHIFT) != 0) {
+ * cursor.setVisible(false);
+ * }
+ * }
+ * });
+ * // Show the TableCursor when the user releases the "SHIFT" or "CTRL" key.
+ * // This signals the end of the multiple selection task.
+ * table.addKeyListener(new KeyAdapter() {
+ * public void keyReleased(KeyEvent e) {
+ * if (e.keyCode == SWT.CONTROL && (e.stateMask & SWT.SHIFT) != 0) return;
+ * if (e.keyCode == SWT.SHIFT && (e.stateMask & SWT.CONTROL) != 0) return;
+ * if (e.keyCode != SWT.CONTROL && (e.stateMask & SWT.CONTROL) != 0) return;
+ * if (e.keyCode != SWT.SHIFT && (e.stateMask & SWT.SHIFT) != 0) return;
+ *
+ * TableItem[] selection = table.getSelection();
+ * TableItem row = (selection.length == 0) ? table.getItem(table.getTopIndex()) : selection[0];
+ * table.showItem(row);
+ * cursor.setSelection(row, 0);
+ * cursor.setVisible(true);
+ * cursor.setFocus();
+ * }
+ * });
+ *
+ * shell.open();
+ * while (!shell.isDisposed()) {
+ * if (!display.readAndDispatch())
+ * display.sleep();
+ * }
+ * display.dispose();
+ * }
+ * </pre></code>
+ *
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>BORDER</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, DefaultSelection</dd>
+ * </dl>
+ *
+ * @since 2.0
+ *
+ */
+public class TableCursor extends Canvas {
+ Table table;
+ int row, column;
+ Listener tableListener, resizeListener;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * table and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a Table control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#BORDER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableCursor(Table parent, int style) {
+ super(parent, style);
+ table = parent;
+ Listener listener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.Dispose :
+ dispose(event);
+ break;
+ case SWT.KeyDown :
+ keyDown(event);
+ break;
+ case SWT.Paint :
+ paint(event);
+ break;
+ case SWT.Traverse :
+ traverse(event);
+ break;
+ }
+ }
+ };
+ addListener(SWT.Dispose, listener);
+ addListener(SWT.KeyDown, listener);
+ addListener(SWT.Paint, listener);
+ addListener(SWT.Traverse, listener);
+
+ tableListener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.MouseDown :
+ tableMouseDown(event);
+ break;
+ case SWT.FocusIn :
+ tableFocusIn(event);
+ break;
+ }
+ }
+ };
+ table.addListener(SWT.FocusIn, tableListener);
+ table.addListener(SWT.MouseDown, tableListener);
+
+ resizeListener = new Listener() {
+ public void handleEvent(Event event) {
+ resize();
+ }
+ };
+ int columns = table.getColumnCount();
+ for (int i = 0; i < columns; i++) {
+ TableColumn column = table.getColumn(i);
+ column.addListener(SWT.Resize, resizeListener);
+ }
+ ScrollBar hBar = table.getHorizontalBar();
+ if (hBar != null) {
+ hBar.addListener(SWT.Selection, resizeListener);
+ }
+ ScrollBar vBar = table.getVerticalBar();
+ if (vBar != null) {
+ vBar.addListener(SWT.Selection, resizeListener);
+ }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's selection changes, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * If the reciever has <code>SWT.CHECK</code> style set and the check selection changes,
+ * the event object detail field contains the value <code>SWT.CHECK</code>.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null)
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+ addListener(SWT.DefaultSelection, typedListener);
+}
+
+void dispose(Event event) {
+ Display display = getDisplay();
+ display.asyncExec(new Runnable() {
+ public void run() {
+ if (table.isDisposed())
+ return;
+ table.removeListener(SWT.FocusIn, tableListener);
+ table.removeListener(SWT.MouseDown, tableListener);
+ int columns = table.getColumnCount();
+ for (int i = 0; i < columns; i++) {
+ TableColumn column = table.getColumn(i);
+ column.removeListener(SWT.Resize, resizeListener);
+ }
+ ScrollBar hBar = table.getHorizontalBar();
+ if (hBar != null) {
+ hBar.removeListener(SWT.Selection, resizeListener);
+ }
+ ScrollBar vBar = table.getVerticalBar();
+ if (vBar != null) {
+ vBar.removeListener(SWT.Selection, resizeListener);
+ }
+ }
+ });
+}
+
+void keyDown(Event event) {
+ switch (event.character) {
+ case SWT.CR :
+ notifyListeners(SWT.DefaultSelection, new Event());
+ return;
+ }
+ switch (event.keyCode) {
+ case SWT.ARROW_UP :
+ setRowColumn(row - 1, column, true);
+ break;
+ case SWT.ARROW_DOWN :
+ setRowColumn(row + 1, column, true);
+ break;
+ case SWT.ARROW_LEFT :
+ setRowColumn(row, column - 1, true);
+ break;
+ case SWT.ARROW_RIGHT :
+ setRowColumn(row, column + 1, true);
+ break;
+ case SWT.HOME :
+ setRowColumn(0, column, true);
+ break;
+ case SWT.END :
+ {
+ int row = table.getItemCount() - 1;
+ setRowColumn(row, column, true);
+ break;
+ }
+ case SWT.PAGE_UP :
+ {
+ int index = table.getTopIndex();
+ if (index == row) {
+ Rectangle rect = table.getClientArea();
+ TableItem item = table.getItem(index);
+ Rectangle itemRect = item.getBounds(0);
+ rect.height -= itemRect.y;
+ int height = table.getItemHeight();
+ int page = Math.max(1, rect.height / height);
+ index = Math.max(0, index - page + 1);
+ }
+ setRowColumn(index, column, true);
+ break;
+ }
+ case SWT.PAGE_DOWN :
+ {
+ int index = table.getTopIndex();
+ Rectangle rect = table.getClientArea();
+ TableItem item = table.getItem(index);
+ Rectangle itemRect = item.getBounds(0);
+ rect.height -= itemRect.y;
+ int height = table.getItemHeight();
+ int page = Math.max(1, rect.height / height);
+ int end = table.getItemCount() - 1;
+ index = Math.min(end, index + page - 1);
+ if (index == row) {
+ index = Math.min(end, index + page - 1);
+ }
+ setRowColumn(index, column, true);
+ break;
+ }
+ }
+}
+
+void paint(Event event) {
+ GC gc = event.gc;
+ Display display = getDisplay();
+ gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));
+ gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
+ gc.fillRectangle(event.x, event.y, event.width, event.height);
+ TableItem item = table.getItem(row);
+ int x = 0, y = 0;
+ Point size = getSize();
+ Image image = item.getImage(column);
+ if (image != null) {
+ Rectangle imageSize = image.getBounds();
+ int imageY = y + (int) (((float) size.y - (float) imageSize.height) / 2.0);
+ gc.drawImage(image, x, imageY);
+ x += imageSize.width;
+ }
+ x += (column == 0) ? 2 : 6;
+
+ int textY =
+ y + (int) (((float) size.y - (float) gc.getFontMetrics().getHeight()) / 2.0);
+ gc.drawString(item.getText(column), x, textY);
+ if (isFocusControl()) {
+ gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+ gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
+ gc.drawFocus(0, 0, size.x, size.y);
+ }
+
+}
+
+void tableFocusIn(Event event) {
+ if (isDisposed())
+ return;
+ if (isVisible())
+ setFocus();
+}
+
+void tableMouseDown(Event event) {
+ if (isDisposed() || !isVisible())
+ return;
+ Point pt = new Point(event.x, event.y);
+ Rectangle clientRect = table.getClientArea();
+ int columns = table.getColumnCount();
+ int start = table.getTopIndex();
+ int end = table.getItemCount();
+ for (int row = start; row < end; row++) {
+ TableItem item = table.getItem(row);
+ for (int column = 0; column < columns; column++) {
+ Rectangle rect = item.getBounds(column);
+ if (rect.y > clientRect.y + clientRect.height)
+ return;
+ if (rect.contains(pt)) {
+ setRowColumn(row, column, true);
+ setFocus();
+ return;
+ }
+ }
+ }
+}
+
+void traverse(Event event) {
+ switch (event.detail) {
+ case SWT.TRAVERSE_ARROW_NEXT :
+ case SWT.TRAVERSE_ARROW_PREVIOUS :
+ case SWT.TRAVERSE_RETURN :
+ event.doit = false;
+ return;
+ }
+ event.doit = true;
+}
+
+void setRowColumn(int row, int column, boolean notify) {
+ if (0 <= row && row < table.getItemCount()) {
+ if (0 <= column && column < table.getColumnCount()) {
+ this.row = row;
+ this.column = column;
+ TableItem item = table.getItem(row);
+ table.showItem(item);
+ setBounds(item.getBounds(column));
+ redraw();
+ if (notify) {
+ notifyListeners(SWT.Selection, new Event());
+ }
+ }
+ }
+}
+
+public void setVisible(boolean visible) {
+ checkWidget();
+ if (visible)
+ resize();
+ super.setVisible(visible);
+}
+
+void resize() {
+ TableItem item = table.getItem(row);
+ setBounds(item.getBounds(column));
+}
+/**
+ * Returns the column over which the TableCursor is positioned.
+ *
+ * @return the column for the current position
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getColumn() {
+ checkWidget();
+ return column;
+}
+/**
+ * Returns the row over which the TableCursor is positioned.
+ *
+ * @return the item for the current position
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem getRow() {
+ checkWidget();
+ return table.getItem(row);
+}
+/**
+ * Positions the TableCursor over the cell at the given row and column in the parent table.
+ *
+ * @param row the index of the row for the cell to select
+ * @param column the index of column for the cell to select
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void setSelection(int row, int column) {
+ checkWidget();
+ if (row < 0
+ || row >= table.getItemCount()
+ || column < 0
+ || column >= table.getColumnCount())
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ setRowColumn(row, column, false);
+}
+/**
+ * Positions the TableCursor over the cell at the given row and column in the parent table.
+ *
+ * @param row the TableItem of the row for the cell to select
+ * @param column the index of column for the cell to select
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void setSelection(TableItem row, int column) {
+ checkWidget();
+ if (row == null
+ || row.isDisposed()
+ || column < 0
+ || column >= table.getColumnCount())
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ setRowColumn(table.indexOf(row), column, false);
+}
} \ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableEditor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableEditor.java
index 4acff4524d..fd6a6c4eae 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableEditor.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableEditor.java
@@ -1,193 +1,193 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
-*
-* A TableEditor is a manager for a Control that appears above a cell in a Table and tracks with the
-* moving and resizing of that cell. It can be used to display a text widget above a cell
-* in a Table so that the user can edit the contents of that cell. It can also be used to display
-* a button that can launch a dialog for modifying the contents of the associated cell.
-*
-* <p> Here is an example of using a TableEditor:
-* <code><pre>
-* Table table = new Table(parent, SWT.FULL_SELECTION);
-* TableEditor editor = new TableEditor (table);
-* table.addSelectionListener (new SelectionAdapter() {
-* public void widgetSelected(SelectionEvent e) {
-*
-* // Clean up any previous editor control
-* Control oldEditor = editor.getEditor();
-* if (oldEditor != null)
-* oldEditor.dispose();
-*
-* // Identify the selected row
-* int index = table.getSelectionIndex ();
-* if (index == -1) return;
-* TableItem item = table.getItem (index);
-*
-* // The control that will be the editor must be a child of the Table
-* Text text = new Text(table, SWT.NONE);
-*
-* //The text editor must have the same size as the cell and must
-* //not be any smaller than 50 pixels.
-* editor.horizontalAlignment = SWT.LEFT;
-* editor.grabHorizontal = true;
-* editor.minimumWidth = 50;
-*
-* // Open the text editor in the second column of the selected row.
-* editor.setEditor (text, item, 1);
-*
-* // Assign focus to the text control
-* text.setFocus ();
-* }
-* });
-* </pre></code>
-*/
-public class TableEditor extends ControlEditor {
-
- Table table;
- TableItem item;
- int column = -1;
- Listener columnListener;
-/**
-* Creates a TableEditor for the specified Table.
-*
-* @param table the Table Control above which this editor will be displayed
-*
-*/
-public TableEditor (Table table) {
- super(table);
- this.table = table;
-
- columnListener = new Listener() {
- public void handleEvent(Event e) {
- resize ();
- }
- };
-
-}
-Rectangle computeBounds () {
- if (item == null || column == -1 || item.isDisposed()) return new Rectangle(0, 0, 0, 0);
-
- Rectangle cell = item.getBounds(column);
- Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, cell.height);
- Rectangle area = table.getClientArea();
- if (cell.x < area.x + area.width) {
- if (cell.x + cell.width > area.x + area.width) {
- cell.width = area.width - cell.x;
- }
- }
-
- if (grabHorizontal){
- editorRect.width = Math.max(cell.width, minimumWidth);
- }
-
- if (horizontalAlignment == SWT.RIGHT) {
- editorRect.x += cell.width - editorRect.width;
- } else if (horizontalAlignment == SWT.LEFT) {
- // do nothing - cell.x is the right answer
- } else { // default is CENTER
- editorRect.x += (cell.width - editorRect.width)/2;
- }
-
- return editorRect;
-}
-/**
- * Removes all associations between the TableEditor and the cell in the table. The
- * Table and the editor Control are <b>not</b> disposed.
- */
-public void dispose () {
-
- if (this.column > -1 && this.column < table.getColumnCount()){
- TableColumn tableColumn = table.getColumn(this.column);
- tableColumn.removeListener(SWT.Resize, columnListener);
- tableColumn.removeListener(SWT.Move, columnListener);
- }
- columnListener = null;
- table = null;
- item = null;
- column = -1;
-
- super.dispose();
-}
-/**
-* Returns the zero based index of the column of the cell being tracked by this editor.
-*
-* @return the zero based index of the column of the cell being tracked by this editor
-*/
-public int getColumn () {
- return column;
-}
-/**
-* Returns the TableItem for the row of the cell being tracked by this editor.
-*
-* @return the TableItem for the row of the cell being tracked by this editor
-*/
-public TableItem getItem () {
- return item;
-}
-public void setColumn(int column) {
-
- int columnCount = table.getColumnCount();
- // Separately handle the case where the table has no TableColumns.
- // In this situation, there is a single default column.
- if (columnCount == 0) {
- this.column = (column == 0) ? 0 : -1;
- resize();
- return;
- }
-
- if (this.column > -1 && this.column < columnCount){
- TableColumn tableColumn = table.getColumn(this.column);
- tableColumn.removeListener(SWT.Resize, columnListener);
- tableColumn.removeListener(SWT.Move, columnListener);
- this.column = -1;
- }
-
- if (column < 0 || column >= table.getColumnCount()) return;
-
- this.column = column;
- TableColumn tableColumn = table.getColumn(this.column);
- tableColumn.addListener(SWT.Resize, columnListener);
- tableColumn.addListener(SWT.Move, columnListener);
- resize();
-}
-public void setItem (TableItem item) {
- this.item = item;
- resize();
-}
-
-/**
-* Specify the Control that is to be displayed and the cell in the table that it is to be positioned above.
-*
-* <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Table control
-* specified in the TableEditor constructor.
-*
-* @param editor the Control that is displayed above the cell being edited
-* @param item the TableItem for the row of the cell being tracked by this editor
-* @param column the zero based index of the column of the cell being tracked by this editor
-*/
-public void setEditor (Control editor, TableItem item, int column) {
- setItem(item);
- setColumn(column);
- setEditor(editor);
-}
-void resize () {
- if (table.isDisposed()) return;
- if (item == null || item.isDisposed()) return;
- int columnCount = table.getColumnCount();
- if (columnCount == 0 && column != 0) return;
- if (columnCount > 0 && (column < 0 || column >= columnCount)) return;
- super.resize();
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+*
+* A TableEditor is a manager for a Control that appears above a cell in a Table and tracks with the
+* moving and resizing of that cell. It can be used to display a text widget above a cell
+* in a Table so that the user can edit the contents of that cell. It can also be used to display
+* a button that can launch a dialog for modifying the contents of the associated cell.
+*
+* <p> Here is an example of using a TableEditor:
+* <code><pre>
+* Table table = new Table(parent, SWT.FULL_SELECTION);
+* TableEditor editor = new TableEditor (table);
+* table.addSelectionListener (new SelectionAdapter() {
+* public void widgetSelected(SelectionEvent e) {
+*
+* // Clean up any previous editor control
+* Control oldEditor = editor.getEditor();
+* if (oldEditor != null)
+* oldEditor.dispose();
+*
+* // Identify the selected row
+* int index = table.getSelectionIndex ();
+* if (index == -1) return;
+* TableItem item = table.getItem (index);
+*
+* // The control that will be the editor must be a child of the Table
+* Text text = new Text(table, SWT.NONE);
+*
+* //The text editor must have the same size as the cell and must
+* //not be any smaller than 50 pixels.
+* editor.horizontalAlignment = SWT.LEFT;
+* editor.grabHorizontal = true;
+* editor.minimumWidth = 50;
+*
+* // Open the text editor in the second column of the selected row.
+* editor.setEditor (text, item, 1);
+*
+* // Assign focus to the text control
+* text.setFocus ();
+* }
+* });
+* </pre></code>
+*/
+public class TableEditor extends ControlEditor {
+
+ Table table;
+ TableItem item;
+ int column = -1;
+ Listener columnListener;
+/**
+* Creates a TableEditor for the specified Table.
+*
+* @param table the Table Control above which this editor will be displayed
+*
+*/
+public TableEditor (Table table) {
+ super(table);
+ this.table = table;
+
+ columnListener = new Listener() {
+ public void handleEvent(Event e) {
+ resize ();
+ }
+ };
+
+}
+Rectangle computeBounds () {
+ if (item == null || column == -1 || item.isDisposed()) return new Rectangle(0, 0, 0, 0);
+
+ Rectangle cell = item.getBounds(column);
+ Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, cell.height);
+ Rectangle area = table.getClientArea();
+ if (cell.x < area.x + area.width) {
+ if (cell.x + cell.width > area.x + area.width) {
+ cell.width = area.width - cell.x;
+ }
+ }
+
+ if (grabHorizontal){
+ editorRect.width = Math.max(cell.width, minimumWidth);
+ }
+
+ if (horizontalAlignment == SWT.RIGHT) {
+ editorRect.x += cell.width - editorRect.width;
+ } else if (horizontalAlignment == SWT.LEFT) {
+ // do nothing - cell.x is the right answer
+ } else { // default is CENTER
+ editorRect.x += (cell.width - editorRect.width)/2;
+ }
+
+ return editorRect;
+}
+/**
+ * Removes all associations between the TableEditor and the cell in the table. The
+ * Table and the editor Control are <b>not</b> disposed.
+ */
+public void dispose () {
+
+ if (this.column > -1 && this.column < table.getColumnCount()){
+ TableColumn tableColumn = table.getColumn(this.column);
+ tableColumn.removeListener(SWT.Resize, columnListener);
+ tableColumn.removeListener(SWT.Move, columnListener);
+ }
+ columnListener = null;
+ table = null;
+ item = null;
+ column = -1;
+
+ super.dispose();
+}
+/**
+* Returns the zero based index of the column of the cell being tracked by this editor.
+*
+* @return the zero based index of the column of the cell being tracked by this editor
+*/
+public int getColumn () {
+ return column;
+}
+/**
+* Returns the TableItem for the row of the cell being tracked by this editor.
+*
+* @return the TableItem for the row of the cell being tracked by this editor
+*/
+public TableItem getItem () {
+ return item;
+}
+public void setColumn(int column) {
+
+ int columnCount = table.getColumnCount();
+ // Separately handle the case where the table has no TableColumns.
+ // In this situation, there is a single default column.
+ if (columnCount == 0) {
+ this.column = (column == 0) ? 0 : -1;
+ resize();
+ return;
+ }
+
+ if (this.column > -1 && this.column < columnCount){
+ TableColumn tableColumn = table.getColumn(this.column);
+ tableColumn.removeListener(SWT.Resize, columnListener);
+ tableColumn.removeListener(SWT.Move, columnListener);
+ this.column = -1;
+ }
+
+ if (column < 0 || column >= table.getColumnCount()) return;
+
+ this.column = column;
+ TableColumn tableColumn = table.getColumn(this.column);
+ tableColumn.addListener(SWT.Resize, columnListener);
+ tableColumn.addListener(SWT.Move, columnListener);
+ resize();
+}
+public void setItem (TableItem item) {
+ this.item = item;
+ resize();
+}
+
+/**
+* Specify the Control that is to be displayed and the cell in the table that it is to be positioned above.
+*
+* <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Table control
+* specified in the TableEditor constructor.
+*
+* @param editor the Control that is displayed above the cell being edited
+* @param item the TableItem for the row of the cell being tracked by this editor
+* @param column the zero based index of the column of the cell being tracked by this editor
+*/
+public void setEditor (Control editor, TableItem item, int column) {
+ setItem(item);
+ setColumn(column);
+ setEditor(editor);
+}
+void resize () {
+ if (table.isDisposed()) return;
+ if (item == null || item.isDisposed()) return;
+ int columnCount = table.getColumnCount();
+ if (columnCount == 0 && column != 0) return;
+ if (columnCount > 0 && (column < 0 || column >= columnCount)) return;
+ super.resize();
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTree.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTree.java
index b53097d7be..54244e3b1f 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTree.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTree.java
@@ -1,742 +1,742 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * A TableTree is a selectable user interface object
- * that displays a hierarchy of items, and issues
- * notification when an item is selected.
- * A TableTree may be single or multi select.
- * <p>
- * The item children that may be added to instances of this class
- * must be of type <code>TableTreeItem</code>.
- * </p><p>
- * Note that although this class is a subclass of <code>Composite</code>,
- * it does not make sense to add <code>Control</code> children to it,
- * or set a layout on it.
- * </p><p>
- * <dl>
- * <dt><b>Styles:</b> <dd> SINGLE, MULTI, CHECK, FULL_SELECTION
- * <dt><b>Events:</b> <dd> Selection, DefaultSelection, Collapse, Expand
- * </dl>
- * <p>
- * Note: Only one of the styles SINGLE, and MULTI may be specified.
- * </p>
- */
-public class TableTree extends Composite {
- Table table;
- TableTreeItem[] items = EMPTY_ITEMS;
- Image plusImage, minusImage, sizeImage;
-
- /*
- * TableTreeItems are not treated as children but rather as items.
- * When the TableTree is disposed, all children are disposed because
- * TableTree inherits this behaviour from Composite. The items
- * must be disposed separately. Because TableTree is not part of
- * the org.eclipse.swt.widgets package, the method releaseWidget can
- * not be overriden (this is how items are disposed of in Table and Tree).
- * Instead, the items are disposed of in response to the dispose event on the
- * TableTree. The "inDispose" flag is used to distinguish between disposing
- * one TableTreeItem (e.g. when removing an entry from the TableTree) and
- * disposing the entire TableTree.
- */
- boolean inDispose = false;
-
- static final TableTreeItem[] EMPTY_ITEMS = new TableTreeItem [0];
- static final String[] EMPTY_TEXTS = new String [0];
- static final Image[] EMPTY_IMAGES = new Image [0];
- static final String ITEMID = "TableTreeItemID"; //$NON-NLS-1$
-
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#SINGLE
- * @see SWT#MULTI
- * @see SWT#CHECK
- * @see SWT#FULL_SELECTION
- * @see #getStyle
- */
-public TableTree(Composite parent, int style) {
- super(parent, SWT.NONE);
- table = new Table(this, style);
- Listener tableListener = new Listener() {
- public void handleEvent(Event e) {
- switch (e.type) {
- case SWT.MouseDown: onMouseDown(e); break;
- case SWT.Selection: onSelection(e); break;
- case SWT.DefaultSelection: onSelection(e); break;
- case SWT.KeyDown: onKeyDown(e); break;
- }
- }
- };
- int[] tableEvents = new int[]{SWT.MouseDown,
- SWT.Selection,
- SWT.DefaultSelection,
- SWT.KeyDown};
- for (int i = 0; i < tableEvents.length; i++) {
- table.addListener(tableEvents[i], tableListener);
- }
-
- Listener listener = new Listener() {
- public void handleEvent(Event e) {
- switch (e.type) {
- case SWT.Dispose: onDispose(e); break;
- case SWT.Resize: onResize(e); break;
- case SWT.FocusIn: onFocusIn(e); break;
- }
- }
- };
- int[] events = new int[]{SWT.Dispose,
- SWT.Resize,
- SWT.FocusIn};
- for (int i = 0; i < events.length; i++) {
- addListener(events[i], listener);
- }
-}
-
-int addItem(TableTreeItem item, int index) {
- if (index < 0 || index > items.length) throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
- TableTreeItem[] newItems = new TableTreeItem[items.length + 1];
- System.arraycopy(items, 0, newItems, 0, index);
- newItems[index] = item;
- System.arraycopy(items, index, newItems, index + 1, items.length - index);
- items = newItems;
-
- /* Return the index in the table where this table should be inserted */
- if (index == items.length - 1 )
- return table.getItemCount();
- else
- return table.indexOf(items[index+1].tableItem);
-}
-
-/**
- * Adds the listener to the collection of listeners who will
- * be notified when the receiver's selection changes, by sending
- * it one of the messages defined in the <code>SelectionListener</code>
- * interface.
- * <p>
- * When <code>widgetSelected</code> is called, the item field of the event object is valid.
- * If the reciever has <code>SWT.CHECK</code> style set and the check selection changes,
- * the event object detail field contains the value <code>SWT.CHECK</code>.
- * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
- * The item field of the event object is valid for default selection, but the detail field is not used.
- * </p>
- *
- * @param listener the listener which should be notified
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @see SelectionListener
- * @see #removeSelectionListener
- * @see SelectionEvent
- */
-public void addSelectionListener(SelectionListener listener) {
- checkWidget();
- if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
- TypedListener typedListener = new TypedListener (listener);
- addListener (SWT.Selection,typedListener);
- addListener (SWT.DefaultSelection,typedListener);
-}
-
-/**
- * Adds the listener to receive tree events.
- * <p>
- *
- * @param listener the tree listener
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_NULL_ARGUMENT when listener is null
- * </ul>
- */
-public void addTreeListener(TreeListener listener) {
- checkWidget();
- if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
- TypedListener typedListener = new TypedListener (listener);
- addListener (SWT.Expand, typedListener);
- addListener (SWT.Collapse, typedListener);
-}
-public Point computeSize (int wHint, int hHint) {
- checkWidget();
- return table.computeSize (wHint, hHint, true);
-}
-public Rectangle computeTrim (int x, int y, int width, int height) {
- checkWidget();
- return table.computeTrim(x, y, width, height);
-}
-
-/**
- * Deselects all items.
- * <p>
- * If an item is selected, it is deselected.
- * If an item is not selected, it remains unselected.
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * </ul>
- */
-public void deselectAll () {
- checkWidget();
- table.deselectAll();
-}
-
-/* Expand upward from the specified leaf item. */
-void expandItem (TableTreeItem item) {
- if (item == null) return;
- expandItem(item.parentItem);
- if (!item.getVisible()) item.setVisible(true);
- if ( !item.expanded && item.items.length > 0) {
- item.setExpanded(true);
- Event event = new Event();
- event.item = item;
- notifyListeners(SWT.Expand, event);
- }
-}
-public Color getBackground () {
- // This method must be overriden otherwise, in a TableTree in which the first
- // item has no sub items, a grey (Widget background colour) square will appear in
- // the first column of the first item.
- // It is not possible in the constructor to set the background of the TableTree
- // to be the same as the background of the Table because this interferes with
- // the TableTree adapting to changes in the System color settings.
- return table.getBackground();
-}
-public Rectangle getClientArea () {
- return table.getClientArea();
-}
-public Color getForeground () {
- return table.getForeground();
-}
-public Font getFont () {
- return table.getFont();
-}
-/**
- * Gets the number of items.
- * <p>
- * @return the number of items in the widget
- */
-public int getItemCount () {
- //checkWidget();
- return items.length;
-}
-
-/**
- * Gets the height of one item.
- * <p>
- * This operation will fail if the height of
- * one item could not be queried from the OS.
- *
- * @return the height of one item in the widget
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_CANNOT_GET_ITEM_HEIGHT when the operation fails
- * </ul>
- */
-public int getItemHeight () {
- checkWidget();
- return table.getItemHeight();
-}
-
-/**
- * Gets the items.
- * <p>
- * @return the items in the widget
- */
-public TableTreeItem [] getItems () {
- //checkWidget();
- TableTreeItem[] newItems = new TableTreeItem[items.length];
- System.arraycopy(items, 0, newItems, 0, items.length);
- return newItems;
-}
-
-/**
- * Gets the selected items.
- * <p>
- * This operation will fail if the selected
- * items cannot be queried from the OS.
- *
- * @return the selected items in the widget
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_CANNOT_GET_SELECTION when the operation fails</li>
- * </ul>
- */
-public TableTreeItem [] getSelection () {
- checkWidget();
- TableItem[] selection = table.getSelection();
- TableTreeItem [] result = new TableTreeItem[selection.length];
- for (int i = 0; i < selection.length; i++){
- result[i] = (TableTreeItem) selection[i].getData(ITEMID);
- }
- return result;
-}
-
-/**
- * Gets the number of selected items.
- * <p>
- * This operation will fail if the number of selected
- * items cannot be queried from the OS.
- *
- * @return the number of selected items in the widget
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_CANNOT_GET_COUNT when the operation fails</li>
- * </ul>
- */
-public int getSelectionCount () {
- checkWidget();
- return table.getSelectionCount();
-}
-
-public int getStyle () {
- checkWidget();
- return table.getStyle();
-}
-
-/**
- * Returns the underlying Table control.
- *
- * @return the underlying Table control
- */
-public Table getTable () {
- //checkWidget();
- return table;
-}
-
-void createImages () {
-
- int itemHeight = sizeImage.getBounds().height;
- // Calculate border around image.
- // At least 9 pixels are needed to draw the image
- // Leave at least a 6 pixel border.
- int indent = Math.min(6, (itemHeight - 9) / 2);
- indent = Math.max(0, indent);
- int size = Math.max (10, itemHeight - 2 * indent);
- size = ((size + 1) / 2) * 2; // size must be an even number
- int midpoint = indent + size / 2;
-
- Color foreground = getForeground();
- Color plusMinus = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
- Color background = getBackground();
-
- /* Plus image */
- PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), plusMinus.getRGB()});
- ImageData imageData = new ImageData(itemHeight, itemHeight, 4, palette);
- imageData.transparentPixel = 1;
- plusImage = new Image(getDisplay(), imageData);
- GC gc = new GC(plusImage);
- gc.setBackground(background);
- gc.fillRectangle(0, 0, itemHeight, itemHeight);
- gc.setForeground(plusMinus);
- gc.drawRectangle(indent, indent, size, size);
- gc.setForeground(foreground);
- gc.drawLine(midpoint, indent + 2, midpoint, indent + size - 2);
- gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);
- gc.dispose();
-
- /* Minus image */
- palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), plusMinus.getRGB()});
- imageData = new ImageData(itemHeight, itemHeight, 4, palette);
- imageData.transparentPixel = 1;
- minusImage = new Image(getDisplay(), imageData);
- gc = new GC(minusImage);
- gc.setBackground(background);
- gc.fillRectangle(0, 0, itemHeight, itemHeight);
- gc.setForeground(plusMinus);
- gc.drawRectangle(indent, indent, size, size);
- gc.setForeground(foreground);
- gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);
- gc.dispose();
-}
-
-Image getPlusImage() {
- if (plusImage == null) createImages();
- return plusImage;
-}
-
-Image getMinusImage() {
- if (minusImage == null) createImages();
- return minusImage;
-}
-
-/**
- * Gets the index of an item.
- *
- * <p>The widget is searched starting at 0 until an
- * item is found that is equal to the search item.
- * If no item is found, -1 is returned. Indexing
- * is zero based. This index is relative to the parent only.
- *
- * @param item the search item
- * @return the index of the item or -1
- */
-public int indexOf (TableTreeItem item) {
- //checkWidget();
- for (int i = 0; i < items.length; i++) {
- if (item == items[i]) return i;
- }
- return -1;
-}
-
-void onDispose(Event e) {
- /*
- * Usually when an item is disposed, destroyItem will change the size of the items array
- * and dispose of the underlying table items.
- * Since the whole table tree is being disposed, this is not necessary. For speed
- * the inDispose flag is used to skip over this part of the item dispose.
- */
- inDispose = true;
- for (int i = 0; i < items.length; i++) {
- items[i].dispose();
- }
- inDispose = false;
- if (plusImage != null) plusImage.dispose();
- if (minusImage != null) minusImage.dispose();
- if (sizeImage != null) sizeImage.dispose();
- plusImage = minusImage = sizeImage = null;
-}
-
-void onResize(Event e) {
- Point size = getSize();
- table.setBounds(0, 0, size.x, size.y);
-}
-
-void onSelection(Event e) {
- Event event = new Event();
- TableItem tableItem = (TableItem)e.item;
- TableTreeItem item = getItem(tableItem);
- event.item = item;
-
- if (e.type == SWT.Selection && e.detail == SWT.CHECK && item != null) {
- event.detail = SWT.CHECK;
- item.checked = tableItem.getChecked();
- }
- notifyListeners(e.type, event);
-}
-/**
- * Returns the item at the given point in the receiver
- * or null if no such item exists. The point is in the
- * coordinate system of the receiver.
- *
- * @param point the point used to locate the item
- * @return the item at the given point
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public TableTreeItem getItem(Point point) {
- checkWidget();
- TableItem item = table.getItem(point);
- if (item == null) return null;
- return getItem(item);
-
-}
-TableTreeItem getItem(TableItem tableItem) {
- if (tableItem == null) return null;
- for (int i = 0; i < items.length; i++) {
- TableTreeItem item = items[i].getItem(tableItem);
- if (item != null) return item;
- }
- return null;
-}
-void onFocusIn (Event e) {
- table.setFocus();
-}
-
-void onKeyDown (Event e) {
- TableTreeItem[] selection = getSelection();
- if (selection.length == 0) return;
- TableTreeItem item = selection[0];
- int type = 0;
- if (e.keyCode == SWT.ARROW_RIGHT || e.keyCode == SWT.ARROW_LEFT) {
- if (e.keyCode == SWT.ARROW_RIGHT) {
- if (item.getItemCount() == 0) return;
- if (item.getExpanded()) {
- TableTreeItem newSelection = item.getItems()[0];
- table.setSelection(new TableItem[]{newSelection.tableItem});
- showItem(newSelection);
- type = SWT.Selection;
- } else {
- item.setExpanded(true);
- type = SWT.Expand;
- }
- } else {
- if (item.getExpanded()) {
- item.setExpanded(false);
- type = SWT.Collapse;
- } else {
- TableTreeItem parent = item.getParentItem();
- if (parent != null) {
- int index = parent.indexOf(item);
- if (index != 0) return;
- table.setSelection(new TableItem[]{parent.tableItem});
- type = SWT.Selection;
- }
- }
- }
- }
- if (e.character == '*') {
- item.expandAll(true);
- }
- if (e.character == '-') {
- if (item.getExpanded()) {
- item.setExpanded(false);
- type = SWT.Collapse;
- }
- }
- if (e.character == '+') {
- if (item.getItemCount() > 0 && !item.getExpanded()) {
- item.setExpanded(true);
- type = SWT.Expand;
- }
- }
- if (type == 0) return;
- Event event = new Event();
- event.item = item;
- notifyListeners(type, event);
-}
-void onMouseDown(Event event) {
- /* If user clicked on the [+] or [-], expand or collapse the tree. */
- TableItem[] items = table.getItems();
- for (int i = 0; i < items.length; i++) {
- Rectangle rect = items[i].getImageBounds(0);
- if (rect.contains(event.x, event.y)) {
- TableTreeItem item = (TableTreeItem) items[i].getData(ITEMID);
- event = new Event();
- event.item = item;
- item.setExpanded(!item.getExpanded());
- if (item.getExpanded()) {
- notifyListeners(SWT.Expand, event);
- } else {
- notifyListeners(SWT.Collapse, event);
- }
- return;
- }
- }
-}
-
-/**
- * Removes all items.
- * <p>
- * This operation will fail when an item
- * could not be removed in the OS.
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_ITEM_NOT_REMOVED when the operation fails
- * </ul>
- */
-public void removeAll () {
- checkWidget();
- setRedraw(false);
- for (int i = items.length - 1; i >= 0; i--) {
- items[i].dispose();
- }
- items = EMPTY_ITEMS;
- setRedraw(true);
-}
-
-void removeItem(TableTreeItem item) {
- int index = 0;
- while (index < items.length && items[index] != item) index++;
- if (index == items.length) return;
- TableTreeItem[] newItems = new TableTreeItem[items.length - 1];
- System.arraycopy(items, 0, newItems, 0, index);
- System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
- items = newItems;
-}
-
-/**
- * Removes the listener.
- * <p>
- *
- * @param listener the listener
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_NULL_ARGUMENT when listener is null
- * </ul>
- */
-public void removeSelectionListener (SelectionListener listener) {
- checkWidget();
- if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
- removeListener(SWT.Selection, listener);
- removeListener(SWT.DefaultSelection, listener);
-}
-
-/**
- * Removes the listener.
- *
- * @param listener the listener
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_NULL_ARGUMENT when listener is null
- * </ul>
- */
-public void removeTreeListener (TreeListener listener) {
- checkWidget();
- if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
- removeListener(SWT.Expand, listener);
- removeListener(SWT.Collapse, listener);
-}
-
-/**
- * Selects all items.
- * <p>
- * If an item is not selected, it is selected.
- * If an item is selected, it remains selected.
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * </ul>
- */
-public void selectAll () {
- checkWidget();
- table.selectAll();
-}
-public void setBackground (Color color) {
- super.setBackground(color);
- table.setBackground(color);
- if (sizeImage != null) {
- GC gc = new GC (sizeImage);
- gc.setBackground(getBackground());
- Rectangle size = sizeImage.getBounds();
- gc.fillRectangle(size);
- gc.dispose();
- }
-}
-public void setEnabled (boolean enabled) {
- super.setEnabled(enabled);
- table.setEnabled(enabled);
-}
-public void setFont (Font font) {
- super.setFont(font);
- table.setFont(font);
-}
-public void setForeground (Color color) {
- super.setForeground(color);
- table.setForeground(color);
-}
-public void setMenu (Menu menu) {
- super.setMenu(menu);
- table.setMenu(menu);
-}
-
-/**
- * Sets the selection.
- * <p>
- * @param items new selection
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_NULL_ARGUMENT when items is null
- * </ul>
- */
-public void setSelection (TableTreeItem[] items) {
- checkWidget();
- TableItem[] tableItems = new TableItem[items.length];
- for (int i = 0; i < items.length; i++) {
- if (items[i] == null) throw new SWTError(SWT.ERROR_NULL_ARGUMENT);
- if (!items[i].getVisible()) expandItem (items[i]);
- tableItems[i] = items[i].tableItem;
- }
- table.setSelection(tableItems);
-}
-public void setToolTipText (String string) {
- super.setToolTipText(string);
- table.setToolTipText(string);
-}
-
-/**
- * Shows the item.
- * <p>
- * @param item the item to be shown
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * <li>ERROR_NULL_ARGUMENT when item is null
- * </ul>
- */
-public void showItem (TableTreeItem item) {
- checkWidget();
- if (item == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
- if (!item.getVisible()) expandItem (item);
- TableItem tableItem = item.tableItem;
- table.showItem(tableItem);
-}
-
-/**
- * Shows the selection.
- * <p>
- * If there is no selection or the selection
- * is already visible, this method does nothing.
- * If the selection is scrolled out of view,
- * the top index of the widget is changed such
- * that selection becomes visible.
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
- * </ul>
- */
-public void showSelection () {
- checkWidget();
- table.showSelection();
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A TableTree is a selectable user interface object
+ * that displays a hierarchy of items, and issues
+ * notification when an item is selected.
+ * A TableTree may be single or multi select.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>TableTreeItem</code>.
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add <code>Control</code> children to it,
+ * or set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b> <dd> SINGLE, MULTI, CHECK, FULL_SELECTION
+ * <dt><b>Events:</b> <dd> Selection, DefaultSelection, Collapse, Expand
+ * </dl>
+ * <p>
+ * Note: Only one of the styles SINGLE, and MULTI may be specified.
+ * </p>
+ */
+public class TableTree extends Composite {
+ Table table;
+ TableTreeItem[] items = EMPTY_ITEMS;
+ Image plusImage, minusImage, sizeImage;
+
+ /*
+ * TableTreeItems are not treated as children but rather as items.
+ * When the TableTree is disposed, all children are disposed because
+ * TableTree inherits this behaviour from Composite. The items
+ * must be disposed separately. Because TableTree is not part of
+ * the org.eclipse.swt.widgets package, the method releaseWidget can
+ * not be overriden (this is how items are disposed of in Table and Tree).
+ * Instead, the items are disposed of in response to the dispose event on the
+ * TableTree. The "inDispose" flag is used to distinguish between disposing
+ * one TableTreeItem (e.g. when removing an entry from the TableTree) and
+ * disposing the entire TableTree.
+ */
+ boolean inDispose = false;
+
+ static final TableTreeItem[] EMPTY_ITEMS = new TableTreeItem [0];
+ static final String[] EMPTY_TEXTS = new String [0];
+ static final Image[] EMPTY_IMAGES = new Image [0];
+ static final String ITEMID = "TableTreeItemID"; //$NON-NLS-1$
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#SINGLE
+ * @see SWT#MULTI
+ * @see SWT#CHECK
+ * @see SWT#FULL_SELECTION
+ * @see #getStyle
+ */
+public TableTree(Composite parent, int style) {
+ super(parent, SWT.NONE);
+ table = new Table(this, style);
+ Listener tableListener = new Listener() {
+ public void handleEvent(Event e) {
+ switch (e.type) {
+ case SWT.MouseDown: onMouseDown(e); break;
+ case SWT.Selection: onSelection(e); break;
+ case SWT.DefaultSelection: onSelection(e); break;
+ case SWT.KeyDown: onKeyDown(e); break;
+ }
+ }
+ };
+ int[] tableEvents = new int[]{SWT.MouseDown,
+ SWT.Selection,
+ SWT.DefaultSelection,
+ SWT.KeyDown};
+ for (int i = 0; i < tableEvents.length; i++) {
+ table.addListener(tableEvents[i], tableListener);
+ }
+
+ Listener listener = new Listener() {
+ public void handleEvent(Event e) {
+ switch (e.type) {
+ case SWT.Dispose: onDispose(e); break;
+ case SWT.Resize: onResize(e); break;
+ case SWT.FocusIn: onFocusIn(e); break;
+ }
+ }
+ };
+ int[] events = new int[]{SWT.Dispose,
+ SWT.Resize,
+ SWT.FocusIn};
+ for (int i = 0; i < events.length; i++) {
+ addListener(events[i], listener);
+ }
+}
+
+int addItem(TableTreeItem item, int index) {
+ if (index < 0 || index > items.length) throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
+ TableTreeItem[] newItems = new TableTreeItem[items.length + 1];
+ System.arraycopy(items, 0, newItems, 0, index);
+ newItems[index] = item;
+ System.arraycopy(items, index, newItems, index + 1, items.length - index);
+ items = newItems;
+
+ /* Return the index in the table where this table should be inserted */
+ if (index == items.length - 1 )
+ return table.getItemCount();
+ else
+ return table.indexOf(items[index+1].tableItem);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's selection changes, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * If the reciever has <code>SWT.CHECK</code> style set and the check selection changes,
+ * the event object detail field contains the value <code>SWT.CHECK</code>.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * The item field of the event object is valid for default selection, but the detail field is not used.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Selection,typedListener);
+ addListener (SWT.DefaultSelection,typedListener);
+}
+
+/**
+ * Adds the listener to receive tree events.
+ * <p>
+ *
+ * @param listener the tree listener
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_NULL_ARGUMENT when listener is null
+ * </ul>
+ */
+public void addTreeListener(TreeListener listener) {
+ checkWidget();
+ if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Expand, typedListener);
+ addListener (SWT.Collapse, typedListener);
+}
+public Point computeSize (int wHint, int hHint) {
+ checkWidget();
+ return table.computeSize (wHint, hHint, true);
+}
+public Rectangle computeTrim (int x, int y, int width, int height) {
+ checkWidget();
+ return table.computeTrim(x, y, width, height);
+}
+
+/**
+ * Deselects all items.
+ * <p>
+ * If an item is selected, it is deselected.
+ * If an item is not selected, it remains unselected.
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * </ul>
+ */
+public void deselectAll () {
+ checkWidget();
+ table.deselectAll();
+}
+
+/* Expand upward from the specified leaf item. */
+void expandItem (TableTreeItem item) {
+ if (item == null) return;
+ expandItem(item.parentItem);
+ if (!item.getVisible()) item.setVisible(true);
+ if ( !item.expanded && item.items.length > 0) {
+ item.setExpanded(true);
+ Event event = new Event();
+ event.item = item;
+ notifyListeners(SWT.Expand, event);
+ }
+}
+public Color getBackground () {
+ // This method must be overriden otherwise, in a TableTree in which the first
+ // item has no sub items, a grey (Widget background colour) square will appear in
+ // the first column of the first item.
+ // It is not possible in the constructor to set the background of the TableTree
+ // to be the same as the background of the Table because this interferes with
+ // the TableTree adapting to changes in the System color settings.
+ return table.getBackground();
+}
+public Rectangle getClientArea () {
+ return table.getClientArea();
+}
+public Color getForeground () {
+ return table.getForeground();
+}
+public Font getFont () {
+ return table.getFont();
+}
+/**
+ * Gets the number of items.
+ * <p>
+ * @return the number of items in the widget
+ */
+public int getItemCount () {
+ //checkWidget();
+ return items.length;
+}
+
+/**
+ * Gets the height of one item.
+ * <p>
+ * This operation will fail if the height of
+ * one item could not be queried from the OS.
+ *
+ * @return the height of one item in the widget
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_CANNOT_GET_ITEM_HEIGHT when the operation fails
+ * </ul>
+ */
+public int getItemHeight () {
+ checkWidget();
+ return table.getItemHeight();
+}
+
+/**
+ * Gets the items.
+ * <p>
+ * @return the items in the widget
+ */
+public TableTreeItem [] getItems () {
+ //checkWidget();
+ TableTreeItem[] newItems = new TableTreeItem[items.length];
+ System.arraycopy(items, 0, newItems, 0, items.length);
+ return newItems;
+}
+
+/**
+ * Gets the selected items.
+ * <p>
+ * This operation will fail if the selected
+ * items cannot be queried from the OS.
+ *
+ * @return the selected items in the widget
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_CANNOT_GET_SELECTION when the operation fails</li>
+ * </ul>
+ */
+public TableTreeItem [] getSelection () {
+ checkWidget();
+ TableItem[] selection = table.getSelection();
+ TableTreeItem [] result = new TableTreeItem[selection.length];
+ for (int i = 0; i < selection.length; i++){
+ result[i] = (TableTreeItem) selection[i].getData(ITEMID);
+ }
+ return result;
+}
+
+/**
+ * Gets the number of selected items.
+ * <p>
+ * This operation will fail if the number of selected
+ * items cannot be queried from the OS.
+ *
+ * @return the number of selected items in the widget
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_CANNOT_GET_COUNT when the operation fails</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+ checkWidget();
+ return table.getSelectionCount();
+}
+
+public int getStyle () {
+ checkWidget();
+ return table.getStyle();
+}
+
+/**
+ * Returns the underlying Table control.
+ *
+ * @return the underlying Table control
+ */
+public Table getTable () {
+ //checkWidget();
+ return table;
+}
+
+void createImages () {
+
+ int itemHeight = sizeImage.getBounds().height;
+ // Calculate border around image.
+ // At least 9 pixels are needed to draw the image
+ // Leave at least a 6 pixel border.
+ int indent = Math.min(6, (itemHeight - 9) / 2);
+ indent = Math.max(0, indent);
+ int size = Math.max (10, itemHeight - 2 * indent);
+ size = ((size + 1) / 2) * 2; // size must be an even number
+ int midpoint = indent + size / 2;
+
+ Color foreground = getForeground();
+ Color plusMinus = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
+ Color background = getBackground();
+
+ /* Plus image */
+ PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), plusMinus.getRGB()});
+ ImageData imageData = new ImageData(itemHeight, itemHeight, 4, palette);
+ imageData.transparentPixel = 1;
+ plusImage = new Image(getDisplay(), imageData);
+ GC gc = new GC(plusImage);
+ gc.setBackground(background);
+ gc.fillRectangle(0, 0, itemHeight, itemHeight);
+ gc.setForeground(plusMinus);
+ gc.drawRectangle(indent, indent, size, size);
+ gc.setForeground(foreground);
+ gc.drawLine(midpoint, indent + 2, midpoint, indent + size - 2);
+ gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);
+ gc.dispose();
+
+ /* Minus image */
+ palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), plusMinus.getRGB()});
+ imageData = new ImageData(itemHeight, itemHeight, 4, palette);
+ imageData.transparentPixel = 1;
+ minusImage = new Image(getDisplay(), imageData);
+ gc = new GC(minusImage);
+ gc.setBackground(background);
+ gc.fillRectangle(0, 0, itemHeight, itemHeight);
+ gc.setForeground(plusMinus);
+ gc.drawRectangle(indent, indent, size, size);
+ gc.setForeground(foreground);
+ gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);
+ gc.dispose();
+}
+
+Image getPlusImage() {
+ if (plusImage == null) createImages();
+ return plusImage;
+}
+
+Image getMinusImage() {
+ if (minusImage == null) createImages();
+ return minusImage;
+}
+
+/**
+ * Gets the index of an item.
+ *
+ * <p>The widget is searched starting at 0 until an
+ * item is found that is equal to the search item.
+ * If no item is found, -1 is returned. Indexing
+ * is zero based. This index is relative to the parent only.
+ *
+ * @param item the search item
+ * @return the index of the item or -1
+ */
+public int indexOf (TableTreeItem item) {
+ //checkWidget();
+ for (int i = 0; i < items.length; i++) {
+ if (item == items[i]) return i;
+ }
+ return -1;
+}
+
+void onDispose(Event e) {
+ /*
+ * Usually when an item is disposed, destroyItem will change the size of the items array
+ * and dispose of the underlying table items.
+ * Since the whole table tree is being disposed, this is not necessary. For speed
+ * the inDispose flag is used to skip over this part of the item dispose.
+ */
+ inDispose = true;
+ for (int i = 0; i < items.length; i++) {
+ items[i].dispose();
+ }
+ inDispose = false;
+ if (plusImage != null) plusImage.dispose();
+ if (minusImage != null) minusImage.dispose();
+ if (sizeImage != null) sizeImage.dispose();
+ plusImage = minusImage = sizeImage = null;
+}
+
+void onResize(Event e) {
+ Point size = getSize();
+ table.setBounds(0, 0, size.x, size.y);
+}
+
+void onSelection(Event e) {
+ Event event = new Event();
+ TableItem tableItem = (TableItem)e.item;
+ TableTreeItem item = getItem(tableItem);
+ event.item = item;
+
+ if (e.type == SWT.Selection && e.detail == SWT.CHECK && item != null) {
+ event.detail = SWT.CHECK;
+ item.checked = tableItem.getChecked();
+ }
+ notifyListeners(e.type, event);
+}
+/**
+ * Returns the item at the given point in the receiver
+ * or null if no such item exists. The point is in the
+ * coordinate system of the receiver.
+ *
+ * @param point the point used to locate the item
+ * @return the item at the given point
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableTreeItem getItem(Point point) {
+ checkWidget();
+ TableItem item = table.getItem(point);
+ if (item == null) return null;
+ return getItem(item);
+
+}
+TableTreeItem getItem(TableItem tableItem) {
+ if (tableItem == null) return null;
+ for (int i = 0; i < items.length; i++) {
+ TableTreeItem item = items[i].getItem(tableItem);
+ if (item != null) return item;
+ }
+ return null;
+}
+void onFocusIn (Event e) {
+ table.setFocus();
+}
+
+void onKeyDown (Event e) {
+ TableTreeItem[] selection = getSelection();
+ if (selection.length == 0) return;
+ TableTreeItem item = selection[0];
+ int type = 0;
+ if (e.keyCode == SWT.ARROW_RIGHT || e.keyCode == SWT.ARROW_LEFT) {
+ if (e.keyCode == SWT.ARROW_RIGHT) {
+ if (item.getItemCount() == 0) return;
+ if (item.getExpanded()) {
+ TableTreeItem newSelection = item.getItems()[0];
+ table.setSelection(new TableItem[]{newSelection.tableItem});
+ showItem(newSelection);
+ type = SWT.Selection;
+ } else {
+ item.setExpanded(true);
+ type = SWT.Expand;
+ }
+ } else {
+ if (item.getExpanded()) {
+ item.setExpanded(false);
+ type = SWT.Collapse;
+ } else {
+ TableTreeItem parent = item.getParentItem();
+ if (parent != null) {
+ int index = parent.indexOf(item);
+ if (index != 0) return;
+ table.setSelection(new TableItem[]{parent.tableItem});
+ type = SWT.Selection;
+ }
+ }
+ }
+ }
+ if (e.character == '*') {
+ item.expandAll(true);
+ }
+ if (e.character == '-') {
+ if (item.getExpanded()) {
+ item.setExpanded(false);
+ type = SWT.Collapse;
+ }
+ }
+ if (e.character == '+') {
+ if (item.getItemCount() > 0 && !item.getExpanded()) {
+ item.setExpanded(true);
+ type = SWT.Expand;
+ }
+ }
+ if (type == 0) return;
+ Event event = new Event();
+ event.item = item;
+ notifyListeners(type, event);
+}
+void onMouseDown(Event event) {
+ /* If user clicked on the [+] or [-], expand or collapse the tree. */
+ TableItem[] items = table.getItems();
+ for (int i = 0; i < items.length; i++) {
+ Rectangle rect = items[i].getImageBounds(0);
+ if (rect.contains(event.x, event.y)) {
+ TableTreeItem item = (TableTreeItem) items[i].getData(ITEMID);
+ event = new Event();
+ event.item = item;
+ item.setExpanded(!item.getExpanded());
+ if (item.getExpanded()) {
+ notifyListeners(SWT.Expand, event);
+ } else {
+ notifyListeners(SWT.Collapse, event);
+ }
+ return;
+ }
+ }
+}
+
+/**
+ * Removes all items.
+ * <p>
+ * This operation will fail when an item
+ * could not be removed in the OS.
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_ITEM_NOT_REMOVED when the operation fails
+ * </ul>
+ */
+public void removeAll () {
+ checkWidget();
+ setRedraw(false);
+ for (int i = items.length - 1; i >= 0; i--) {
+ items[i].dispose();
+ }
+ items = EMPTY_ITEMS;
+ setRedraw(true);
+}
+
+void removeItem(TableTreeItem item) {
+ int index = 0;
+ while (index < items.length && items[index] != item) index++;
+ if (index == items.length) return;
+ TableTreeItem[] newItems = new TableTreeItem[items.length - 1];
+ System.arraycopy(items, 0, newItems, 0, index);
+ System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
+ items = newItems;
+}
+
+/**
+ * Removes the listener.
+ * <p>
+ *
+ * @param listener the listener
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_NULL_ARGUMENT when listener is null
+ * </ul>
+ */
+public void removeSelectionListener (SelectionListener listener) {
+ checkWidget();
+ if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
+ removeListener(SWT.Selection, listener);
+ removeListener(SWT.DefaultSelection, listener);
+}
+
+/**
+ * Removes the listener.
+ *
+ * @param listener the listener
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_NULL_ARGUMENT when listener is null
+ * </ul>
+ */
+public void removeTreeListener (TreeListener listener) {
+ checkWidget();
+ if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
+ removeListener(SWT.Expand, listener);
+ removeListener(SWT.Collapse, listener);
+}
+
+/**
+ * Selects all items.
+ * <p>
+ * If an item is not selected, it is selected.
+ * If an item is selected, it remains selected.
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * </ul>
+ */
+public void selectAll () {
+ checkWidget();
+ table.selectAll();
+}
+public void setBackground (Color color) {
+ super.setBackground(color);
+ table.setBackground(color);
+ if (sizeImage != null) {
+ GC gc = new GC (sizeImage);
+ gc.setBackground(getBackground());
+ Rectangle size = sizeImage.getBounds();
+ gc.fillRectangle(size);
+ gc.dispose();
+ }
+}
+public void setEnabled (boolean enabled) {
+ super.setEnabled(enabled);
+ table.setEnabled(enabled);
+}
+public void setFont (Font font) {
+ super.setFont(font);
+ table.setFont(font);
+}
+public void setForeground (Color color) {
+ super.setForeground(color);
+ table.setForeground(color);
+}
+public void setMenu (Menu menu) {
+ super.setMenu(menu);
+ table.setMenu(menu);
+}
+
+/**
+ * Sets the selection.
+ * <p>
+ * @param items new selection
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_NULL_ARGUMENT when items is null
+ * </ul>
+ */
+public void setSelection (TableTreeItem[] items) {
+ checkWidget();
+ TableItem[] tableItems = new TableItem[items.length];
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] == null) throw new SWTError(SWT.ERROR_NULL_ARGUMENT);
+ if (!items[i].getVisible()) expandItem (items[i]);
+ tableItems[i] = items[i].tableItem;
+ }
+ table.setSelection(tableItems);
+}
+public void setToolTipText (String string) {
+ super.setToolTipText(string);
+ table.setToolTipText(string);
+}
+
+/**
+ * Shows the item.
+ * <p>
+ * @param item the item to be shown
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * <li>ERROR_NULL_ARGUMENT when item is null
+ * </ul>
+ */
+public void showItem (TableTreeItem item) {
+ checkWidget();
+ if (item == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
+ if (!item.getVisible()) expandItem (item);
+ TableItem tableItem = item.tableItem;
+ table.showItem(tableItem);
+}
+
+/**
+ * Shows the selection.
+ * <p>
+ * If there is no selection or the selection
+ * is already visible, this method does nothing.
+ * If the selection is scrolled out of view,
+ * the top index of the widget is changed such
+ * that selection becomes visible.
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
+ * </ul>
+ */
+public void showSelection () {
+ checkWidget();
+ table.showSelection();
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeEditor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeEditor.java
index b00f5a18f6..8a127d5a91 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeEditor.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeEditor.java
@@ -1,231 +1,231 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.events.*;
-/**
-*
-* A TableTreeEditor is a manager for a Control that appears above a cell in a TableTree
-* and tracks with the moving and resizing of that cell. It can be used to display a
-* text widget above a cell in a TableTree so that the user can edit the contents of
-* that cell. It can also be used to display a button that can launch a dialog for
-* modifying the contents of the associated cell.
-*
-* <p> Here is an example of using a TableTreeEditor:
-* <code><pre>
-* public static void main (String [] args) {
-* Display display = new Display ();
-* Shell shell = new Shell (display);
-* final TableTree tableTree = new TableTree(shell, SWT.FULL_SELECTION);
-* Table table = tableTree.getTable();
-* table.setLinesVisible(true);
-* TableColumn column1 = new TableColumn(table, SWT.NONE);
-* column1.setText("column 1");
-* TableColumn column2 = new TableColumn(table, SWT.NONE);
-* column2.setText("column 2");
-* for (int i = 0; i < 40; i++) {
-* TableTreeItem item = new TableTreeItem(tableTree, SWT.NONE);
-* item.setText(0, "table tree item"+i);
-* item.setText(1, "value "+i);
-* }
-* column1.pack();
-* column2.pack();
-* final TableTreeEditor editor = new TableTreeEditor (tableTree);
-* tableTree.addSelectionListener (new SelectionAdapter() {
-* public void widgetSelected(SelectionEvent e) {
-* // Clean up any previous editor control
-* Control oldEditor = editor.getEditor();
-* if (oldEditor != null)
-* oldEditor.dispose();
-* // Identify the selected row
-* TableTreeItem[] selection = tableTree.getSelection();
-* if (selection.length == 0) return;
-* TableTreeItem item = selection[0];
-* // The control that will be the editor must be a child of the Table
-* // that underlies the TableTree
-* Text text = new Text(tableTree.getTable(), SWT.NONE);
-* //text.moveAbove(tableTree);
-* //The text editor must have the same size as the cell and must
-* //not be any smaller than 50 pixels.
-* editor.horizontalAlignment = SWT.LEFT;
-* editor.grabHorizontal = true;
-* //editor.minimumWidth = 50;
-* // Open the text editor in the second column of the selected row.
-* editor.setEditor (text, item, 1);
-* // Assign focus to the text control
-* text.setFocus ();
-* }
-* });
-* tableTree.setBounds(10, 10, 200, 400);
-* shell.open ();
-* while (!shell.isDisposed ()) {
-* if (!display.readAndDispatch ()) display.sleep ();
-* }
-* display.dispose ();
-* }
-* </pre></code>
-*/
-public class TableTreeEditor extends ControlEditor {
-
- TableTree tableTree;
- TableTreeItem item;
- int column = -1;
- ControlListener columnListener;
- TreeListener treeListener;
-/**
-* Creates a TableEditor for the specified Table.
-*
-* @param table the Table Control above which this editor will be displayed
-*
-*/
-public TableTreeEditor (TableTree tableTree) {
- super(tableTree.getTable());
- this.tableTree = tableTree;
-
- treeListener = new TreeListener () {
- final Runnable runnable = new Runnable() {
- public void run() {
- if (TableTreeEditor.this.tableTree.isDisposed() || editor == null) return;
- resize();
- editor.setVisible(true);
- }
- };
- public void treeCollapsed(TreeEvent e) {
- if (editor == null) return;
- editor.setVisible(false);
- Display display = TableTreeEditor.this.tableTree.getDisplay();
- display.asyncExec(runnable);
- }
- public void treeExpanded(TreeEvent e) {
- if (editor == null) return;
- editor.setVisible(false);
- Display display = TableTreeEditor.this.tableTree.getDisplay();
- display.asyncExec(runnable);
- }
- };
- tableTree.addTreeListener(treeListener);
-
- columnListener = new ControlListener() {
- public void controlMoved(ControlEvent e){
- resize ();
- }
- public void controlResized(ControlEvent e){
- resize ();
- }
- };
-
-}
-Rectangle computeBounds () {
- if (item == null || column == -1 || item.isDisposed() || item.tableItem == null) return new Rectangle(0, 0, 0, 0);
- Rectangle cell = item.getBounds(column);
- Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, cell.height);
- Rectangle area = tableTree.getClientArea();
- if (cell.x < area.x + area.width) {
- if (cell.x + cell.width > area.x + area.width) {
- cell.width = area.width - cell.x;
- }
- }
-
- if (grabHorizontal){
- editorRect.width = Math.max(cell.width, minimumWidth);
- }
-
- if (horizontalAlignment == SWT.RIGHT) {
- editorRect.x += cell.width - editorRect.width;
- } else if (horizontalAlignment == SWT.LEFT) {
- // do nothing - cell.x is the right answer
- } else { // default is CENTER
- editorRect.x += (cell.width - editorRect.width)/2;
- }
-
- return editorRect;
-}
-/**
- * Removes all associations between the TableEditor and the cell in the table. The
- * Table and the editor Control are <b>not</b> disposed.
- */
-public void dispose () {
-
- if (treeListener != null)
- tableTree.removeTreeListener(treeListener);
- treeListener = null;
- Table table = tableTree.getTable();
- if (this.column > -1 && this.column < table.getColumnCount()){
- TableColumn tableColumn = table.getColumn(this.column);
- tableColumn.removeControlListener(columnListener);
- }
-
- tableTree = null;
- item = null;
- column = -1;
-
- super.dispose();
-}
-/**
-* Returns the zero based index of the column of the cell being tracked by this editor.
-*
-* @return the zero based index of the column of the cell being tracked by this editor
-*/
-public int getColumn () {
- return column;
-}
-public void setColumn(int column) {
- Table table = tableTree.getTable();
- if (this.column > -1 && this.column < table.getColumnCount()){
- TableColumn tableColumn = table.getColumn(this.column);
- tableColumn.removeControlListener(columnListener);
- this.column = -1;
- }
-
- if (column < 0 || column >= table.getColumnCount()) return;
-
- this.column = column;
- TableColumn tableColumn = table.getColumn(this.column);
- tableColumn.addControlListener(columnListener);
-
- resize();
-}
-/**
-* Returns the TableItem for the row of the cell being tracked by this editor.
-*
-* @return the TableItem for the row of the cell being tracked by this editor
-*/
-public TableTreeItem getItem () {
- return item;
-}
-public void setItem (TableTreeItem item) {
- this.item = item;
- resize();
-}
-
-/**
-* Specify the Control that is to be displayed and the cell in the table that it is to be positioned above.
-*
-* <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Table control
-* specified in the TableEditor constructor.
-*
-* @param editor the Control that is displayed above the cell being edited
-* @param item the TableItem for the row of the cell being tracked by this editor
-* @param column the zero based index of the column of the cell being tracked by this editor
-*/
-public void setEditor (Control editor, TableTreeItem item, int column) {
- setItem(item);
- setColumn(column);
- super.setEditor(editor);
-}
-void resize () {
- if (tableTree.isDisposed()) return;
- if (item == null || item.isDisposed()) return;
- Table table = tableTree.getTable();
- if (column < 0 || column >= table.getColumnCount()) return;
- super.resize();
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.events.*;
+/**
+*
+* A TableTreeEditor is a manager for a Control that appears above a cell in a TableTree
+* and tracks with the moving and resizing of that cell. It can be used to display a
+* text widget above a cell in a TableTree so that the user can edit the contents of
+* that cell. It can also be used to display a button that can launch a dialog for
+* modifying the contents of the associated cell.
+*
+* <p> Here is an example of using a TableTreeEditor:
+* <code><pre>
+* public static void main (String [] args) {
+* Display display = new Display ();
+* Shell shell = new Shell (display);
+* final TableTree tableTree = new TableTree(shell, SWT.FULL_SELECTION);
+* Table table = tableTree.getTable();
+* table.setLinesVisible(true);
+* TableColumn column1 = new TableColumn(table, SWT.NONE);
+* column1.setText("column 1");
+* TableColumn column2 = new TableColumn(table, SWT.NONE);
+* column2.setText("column 2");
+* for (int i = 0; i < 40; i++) {
+* TableTreeItem item = new TableTreeItem(tableTree, SWT.NONE);
+* item.setText(0, "table tree item"+i);
+* item.setText(1, "value "+i);
+* }
+* column1.pack();
+* column2.pack();
+* final TableTreeEditor editor = new TableTreeEditor (tableTree);
+* tableTree.addSelectionListener (new SelectionAdapter() {
+* public void widgetSelected(SelectionEvent e) {
+* // Clean up any previous editor control
+* Control oldEditor = editor.getEditor();
+* if (oldEditor != null)
+* oldEditor.dispose();
+* // Identify the selected row
+* TableTreeItem[] selection = tableTree.getSelection();
+* if (selection.length == 0) return;
+* TableTreeItem item = selection[0];
+* // The control that will be the editor must be a child of the Table
+* // that underlies the TableTree
+* Text text = new Text(tableTree.getTable(), SWT.NONE);
+* //text.moveAbove(tableTree);
+* //The text editor must have the same size as the cell and must
+* //not be any smaller than 50 pixels.
+* editor.horizontalAlignment = SWT.LEFT;
+* editor.grabHorizontal = true;
+* //editor.minimumWidth = 50;
+* // Open the text editor in the second column of the selected row.
+* editor.setEditor (text, item, 1);
+* // Assign focus to the text control
+* text.setFocus ();
+* }
+* });
+* tableTree.setBounds(10, 10, 200, 400);
+* shell.open ();
+* while (!shell.isDisposed ()) {
+* if (!display.readAndDispatch ()) display.sleep ();
+* }
+* display.dispose ();
+* }
+* </pre></code>
+*/
+public class TableTreeEditor extends ControlEditor {
+
+ TableTree tableTree;
+ TableTreeItem item;
+ int column = -1;
+ ControlListener columnListener;
+ TreeListener treeListener;
+/**
+* Creates a TableEditor for the specified Table.
+*
+* @param table the Table Control above which this editor will be displayed
+*
+*/
+public TableTreeEditor (TableTree tableTree) {
+ super(tableTree.getTable());
+ this.tableTree = tableTree;
+
+ treeListener = new TreeListener () {
+ final Runnable runnable = new Runnable() {
+ public void run() {
+ if (TableTreeEditor.this.tableTree.isDisposed() || editor == null) return;
+ resize();
+ editor.setVisible(true);
+ }
+ };
+ public void treeCollapsed(TreeEvent e) {
+ if (editor == null) return;
+ editor.setVisible(false);
+ Display display = TableTreeEditor.this.tableTree.getDisplay();
+ display.asyncExec(runnable);
+ }
+ public void treeExpanded(TreeEvent e) {
+ if (editor == null) return;
+ editor.setVisible(false);
+ Display display = TableTreeEditor.this.tableTree.getDisplay();
+ display.asyncExec(runnable);
+ }
+ };
+ tableTree.addTreeListener(treeListener);
+
+ columnListener = new ControlListener() {
+ public void controlMoved(ControlEvent e){
+ resize ();
+ }
+ public void controlResized(ControlEvent e){
+ resize ();
+ }
+ };
+
+}
+Rectangle computeBounds () {
+ if (item == null || column == -1 || item.isDisposed() || item.tableItem == null) return new Rectangle(0, 0, 0, 0);
+ Rectangle cell = item.getBounds(column);
+ Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, cell.height);
+ Rectangle area = tableTree.getClientArea();
+ if (cell.x < area.x + area.width) {
+ if (cell.x + cell.width > area.x + area.width) {
+ cell.width = area.width - cell.x;
+ }
+ }
+
+ if (grabHorizontal){
+ editorRect.width = Math.max(cell.width, minimumWidth);
+ }
+
+ if (horizontalAlignment == SWT.RIGHT) {
+ editorRect.x += cell.width - editorRect.width;
+ } else if (horizontalAlignment == SWT.LEFT) {
+ // do nothing - cell.x is the right answer
+ } else { // default is CENTER
+ editorRect.x += (cell.width - editorRect.width)/2;
+ }
+
+ return editorRect;
+}
+/**
+ * Removes all associations between the TableEditor and the cell in the table. The
+ * Table and the editor Control are <b>not</b> disposed.
+ */
+public void dispose () {
+
+ if (treeListener != null)
+ tableTree.removeTreeListener(treeListener);
+ treeListener = null;
+ Table table = tableTree.getTable();
+ if (this.column > -1 && this.column < table.getColumnCount()){
+ TableColumn tableColumn = table.getColumn(this.column);
+ tableColumn.removeControlListener(columnListener);
+ }
+
+ tableTree = null;
+ item = null;
+ column = -1;
+
+ super.dispose();
+}
+/**
+* Returns the zero based index of the column of the cell being tracked by this editor.
+*
+* @return the zero based index of the column of the cell being tracked by this editor
+*/
+public int getColumn () {
+ return column;
+}
+public void setColumn(int column) {
+ Table table = tableTree.getTable();
+ if (this.column > -1 && this.column < table.getColumnCount()){
+ TableColumn tableColumn = table.getColumn(this.column);
+ tableColumn.removeControlListener(columnListener);
+ this.column = -1;
+ }
+
+ if (column < 0 || column >= table.getColumnCount()) return;
+
+ this.column = column;
+ TableColumn tableColumn = table.getColumn(this.column);
+ tableColumn.addControlListener(columnListener);
+
+ resize();
+}
+/**
+* Returns the TableItem for the row of the cell being tracked by this editor.
+*
+* @return the TableItem for the row of the cell being tracked by this editor
+*/
+public TableTreeItem getItem () {
+ return item;
+}
+public void setItem (TableTreeItem item) {
+ this.item = item;
+ resize();
+}
+
+/**
+* Specify the Control that is to be displayed and the cell in the table that it is to be positioned above.
+*
+* <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Table control
+* specified in the TableEditor constructor.
+*
+* @param editor the Control that is displayed above the cell being edited
+* @param item the TableItem for the row of the cell being tracked by this editor
+* @param column the zero based index of the column of the cell being tracked by this editor
+*/
+public void setEditor (Control editor, TableTreeItem item, int column) {
+ setItem(item);
+ setColumn(column);
+ super.setEditor(editor);
+}
+void resize () {
+ if (tableTree.isDisposed()) return;
+ if (item == null || item.isDisposed()) return;
+ Table table = tableTree.getTable();
+ if (column < 0 || column >= table.getColumnCount()) return;
+ super.resize();
+}
} \ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeItem.java
index 4b6f0ca1b3..6bd93abf20 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeItem.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TableTreeItem.java
@@ -1,803 +1,803 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-
-/**
- * A TableTreeItem is a selectable user interface object
- * that represents an item in a heirarchy of items in a
- * TableTree.
- */
-public class TableTreeItem extends Item {
- TableItem tableItem;
- TableTree parent;
- TableTreeItem parentItem;
- TableTreeItem [] items = TableTree.EMPTY_ITEMS;
- String[] texts = TableTree.EMPTY_TEXTS;
- Image[] images = TableTree.EMPTY_IMAGES;
- Color background;
- Color foreground;
- boolean expanded;
- boolean checked;
- boolean grayed;
-
-/**
- * Constructs a new instance of this class given its parent
- * (which must be a <code>TableTree</code>)
- * and a style value describing its behavior and appearance.
- * The item is added to the end of the items maintained by its parent.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a composite control which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT
- * @see Widget#getStyle
- */
-public TableTreeItem(TableTree parent, int style) {
- this (parent, style, parent.getItemCount());
-}
-
-/**
- * Constructs a new instance of this class given its parent
- * (which must be a <code>TableTree</code>,
- * a style value describing its behavior and appearance, and the index
- * at which to place it in the items maintained by its parent.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a composite control which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- * @param index the index to store the receiver in its parent
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT
- * @see Widget#getStyle
- */
-public TableTreeItem(TableTree parent, int style, int index) {
- this (parent, null, style, index);
-}
-
-/**
- * Constructs a new instance of this class given its parent
- * (which must be a <code>TableTreeItem</code>)
- * and a style value describing its behavior and appearance.
- * The item is added to the end of the items maintained by its parent.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parentItem a composite control which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT
- * @see Widget#getStyle
- */
-public TableTreeItem(TableTreeItem parent, int style) {
- this (parent, style, parent.getItemCount());
-}
-
-/**
- * Constructs a new instance of this class given its parent
- * (which must be a <code>TableTreeItem</code>),
- * a style value describing its behavior and appearance, and the index
- * at which to place it in the items maintained by its parent.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parentItem a composite control which will be the parent of the new instance (cannot be null)
- * @param style the style of control to construct
- * @param index the index to store the receiver in its parent
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT
- * @see Widget#getStyle
- */
-public TableTreeItem(TableTreeItem parent, int style, int index) {
- this (parent.getParent(), parent, style, index);
-}
-
-TableTreeItem(TableTree parent, TableTreeItem parentItem, int style, int index) {
- super(parent, style);
- this.parent = parent;
- this.parentItem = parentItem;
- if (parentItem == null) {
-
- /* Root items are visible immediately */
- int tableIndex = parent.addItem(this, index);
- tableItem = new TableItem(parent.getTable(), style, tableIndex);
- tableItem.setData(TableTree.ITEMID, this);
- addCheck();
- /*
- * Feature in the Table. The table uses the first image that
- * is inserted into the table to size the table rows. If the
- * user is allowed to insert the first image, this will cause
- * the +/- images to be scaled. The fix is to insert a dummy
- * image to force the size.
- */
- if (parent.sizeImage == null) {
- int itemHeight = parent.getItemHeight();
- parent.sizeImage = new Image(null, itemHeight, itemHeight);
- GC gc = new GC (parent.sizeImage);
- gc.setBackground(parent.getBackground());
- gc.fillRectangle(0, 0, itemHeight, itemHeight);
- gc.dispose();
- tableItem.setImage(0, parent.sizeImage);
- }
- } else {
- parentItem.addItem(this, index);
- }
-}
-void addCheck() {
- Table table = parent.getTable();
- if ((table.getStyle() & SWT.CHECK) == 0) return;
- tableItem.setChecked(checked);
- tableItem.setGrayed(grayed);
-}
-void addItem(TableTreeItem item, int index) {
- if (item == null) throw new SWTError(SWT.ERROR_NULL_ARGUMENT);
- if (index < 0 || index > items.length) throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
-
- /* Now that item has a sub-node it must indicate that it can be expanded */
- if (items.length == 0 && index == 0) {
- if (tableItem != null) {
- Image image = expanded ? parent.getMinusImage() : parent.getPlusImage();
- tableItem.setImage(0, image);
- }
- }
-
- /* Put the item in the items list */
- TableTreeItem[] newItems = new TableTreeItem[items.length + 1];
- System.arraycopy(items, 0, newItems, 0, index);
- newItems[index] = item;
- System.arraycopy(items, index, newItems, index + 1, items.length - index);
- items = newItems;
- if (expanded) item.setVisible(true);
-}
-
-/**
- * Returns the receiver's background color.
- *
- * @return the background color
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.0
- *
- */
-public Color getBackground () {
- checkWidget ();
- return (background == null) ? parent.getBackground() : background;
-}
-
-/**
- * Returns a rectangle describing the receiver's size and location
- * relative to its parent.
- *
- * @return the receiver's bounding rectangle
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public Rectangle getBounds (int index) {
- checkWidget();
- if (tableItem != null) {
- return tableItem.getBounds(index);
- } else {
- return new Rectangle(0, 0, 0, 0);
- }
-}
-/**
- * Returns <code>true</code> if the receiver is checked,
- * and false otherwise. When the parent does not have
- * the <code>CHECK style, return false.
- *
- * @return the checked state of the checkbox
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public boolean getChecked () {
- checkWidget();
- if (tableItem == null) return checked;
- return tableItem.getChecked();
-}
-
-/**
- * Returns <code>true</code> if the receiver is grayed,
- * and false otherwise. When the parent does not have
- * the <code>CHECK</code> style, return false.
- *
- * @return the grayed state of the checkbox
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.1
- */
-public boolean getGrayed () {
- checkWidget();
- if (tableItem == null) return grayed;
- return tableItem.getGrayed();
-}
-
-public Display getDisplay () {
- TableTree parent = this.parent;
- if (parent == null) throw new SWTError (SWT.ERROR_WIDGET_DISPOSED);
- return parent.getDisplay ();
-}
-
-/**
- * Returns <code>true</code> if the receiver is expanded,
- * and false otherwise.
- * <p>
- *
- * @return the expanded state
- */
-public boolean getExpanded () {
- //checkWidget();
- return expanded;
-}
-
-/**
- * Returns the foreground color that the receiver will use to draw.
- *
- * @return the receiver's foreground color
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.0
- *
- */
-public Color getForeground () {
- checkWidget ();
- return (foreground == null) ? parent.getForeground() : foreground;
-}
-/**
- * Gets the first image.
- * <p>
- * The image in column 0 is reserved for the [+] and [-]
- * images of the tree, therefore getImage(0) will return null.
- *
- * @return the image at index 0
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public Image getImage () {
- checkWidget();
- return getImage(0);
-}
-
-/**
- * Gets the image at the specified index.
- * <p>
- * Indexing is zero based. The image can be null.
- * The image in column 0 is reserved for the [+] and [-]
- * images of the tree, therefore getImage(0) will return null.
- * Return null if the index is out of range.
- *
- * @param index the index of the image
- * @return the image at the specified index or null
- */
-public Image getImage (int index) {
- //checkWidget();
- if (0 < index && index < images.length) return images[index];
- return null;
-}
-
-int getIndent() {
- if (parentItem == null) return 0;
- return parentItem.getIndent() + 1;
-}
-
-/**
- * Returns the number of items contained in the receiver
- * that are direct item children of the receiver.
- *
- * @return the number of items
- */
-public int getItemCount () {
- //checkWidget();
- return items.length;
-}
-
-/**
- * Returns an array of <code>TableTreeItem</code>s which are the
- * direct item children of the receiver.
- * <p>
- * Note: This is not the actual structure used by the receiver
- * to maintain its list of items, so modifying the array will
- * not affect the receiver.
- * </p>
- *
- * @return the receiver's items
- */
-public TableTreeItem[] getItems () {
- //checkWidget();
- TableTreeItem[] newItems = new TableTreeItem[items.length];
- System.arraycopy(items, 0, newItems, 0, items.length);
- return newItems;
-}
-
-TableTreeItem getItem(TableItem tableItem) {
- if (tableItem == null) return null;
- if (this.tableItem == tableItem) return this;
- for (int i = 0; i < items.length; i++) {
- TableTreeItem item = items[i].getItem(tableItem);
- if (item != null) return item;
- }
- return null;
-}
-
-/**
- * Returns the receiver's parent, which must be a <code>TableTree</code>.
- *
- * @return the receiver's parent
- */
-public TableTree getParent () {
- //checkWidget();
- return parent;
-}
-
-/**
- * Returns the receiver's parent item, which must be a
- * <code>TableTreeItem</code> or null when the receiver is a
- * root.
- *
- * @return the receiver's parent item
- */
-public TableTreeItem getParentItem () {
- //checkWidget();
- return parentItem;
-}
-public String getText () {
- checkWidget();
- return getText(0);
-}
-
-/**
- * Gets the item text at the specified index.
- * <p>
- * Indexing is zero based.
- *
- * This operation will fail when the index is out
- * of range or an item could not be queried from
- * the OS.
- *
- * @param index the index of the item
- * @return the item text at the specified index, which can be null
- */
-public String getText(int index) {
- //checkWidget();
- if (0 <= index && index < texts.length) return texts[index];
- return null;
-}
-
-boolean getVisible () {
- return tableItem != null;
-}
-
-/**
- * Gets the index of the specified item.
- *
- * <p>The widget is searched starting at 0 until an
- * item is found that is equal to the search item.
- * If no item is found, -1 is returned. Indexing
- * is zero based. This index is relative to the parent only.
- *
- * @param item the search item
- * @return the index of the item or -1 if the item is not found
- *
- */
-public int indexOf (TableTreeItem item) {
- //checkWidget();
- for (int i = 0; i < items.length; i++) {
- if (items[i] == item) return i;
- }
- return -1;
-}
-
-void expandAll(boolean notify) {
- if (items.length == 0) return;
- if (!expanded) {
- setExpanded(true);
- if (notify) {
- Event event = new Event();
- event.item = this;
- parent.notifyListeners(SWT.Expand, event);
- }
- }
- for (int i = 0; i < items.length; i++) {
- items[i].expandAll(notify);
- }
-}
-int expandedIndexOf (TableTreeItem item) {
- int index = 0;
- for (int i = 0; i < items.length; i++) {
- if (items[i] == item) return index;
- if (items[i].expanded) index += items[i].visibleChildrenCount ();
- index++;
- }
- return -1;
-}
-
-int visibleChildrenCount () {
- int count = 0;
- for (int i = 0; i < items.length; i++) {
- if (items[i].getVisible ()) {
- count += 1 + items[i].visibleChildrenCount ();
- }
- }
- return count;
-}
-
-public void dispose () {
- if (isDisposed()) return;
- for (int i = items.length - 1; i >= 0; i--) {
- items[i].dispose();
- }
- super.dispose();
- if (!parent.inDispose) {
- if (parentItem != null) {
- parentItem.removeItem(this);
- } else {
- parent.removeItem(this);
- }
- if (tableItem != null) tableItem.dispose();
- }
- items = null;
- parentItem = null;
- parent = null;
- images = null;
- texts = null;
- tableItem = null;
- foreground = null;
- background = null;
-}
-
-void removeItem(TableTreeItem item) {
- int index = 0;
- while (index < items.length && items[index] != item) index++;
- if (index == items.length) return;
- TableTreeItem[] newItems = new TableTreeItem[items.length - 1];
- System.arraycopy(items, 0, newItems, 0, index);
- System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
- items = newItems;
- if (items.length == 0) {
- if (tableItem != null) tableItem.setImage(0, null);
- }
-}
-
-/**
- * Sets the receiver's background color to the color specified
- * by the argument, or to the default system color for the item
- * if the argument is null.
- *
- * @param color the new color (or null)
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.0
- *
- */
-public void setBackground (Color color) {
- checkWidget ();
- if (color != null && color.isDisposed ()) {
- SWT.error (SWT.ERROR_INVALID_ARGUMENT);
- }
- if (tableItem != null) {
- tableItem.setBackground(color);
- }
- background = color;
-}
-
-/**
- * Sets the checked state of the checkbox for this item. This state change
- * only applies if the Table was created with the SWT.CHECK style.
- *
- * @param checked the new checked state of the checkbox
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setChecked (boolean checked) {
- checkWidget();
- Table table = parent.getTable();
- if ((table.getStyle() & SWT.CHECK) == 0) return;
- if (tableItem != null) {
- tableItem.setChecked(checked);
- }
- this.checked = checked;
-}
-
-/**
- * Sets the grayed state of the checkbox for this item. This state change
- * only applies if the Table was created with the SWT.CHECK style.
- *
- * @param grayed the new grayed state of the checkbox;
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.1
- */
-public void setGrayed (boolean grayed) {
- checkWidget();
- Table table = parent.getTable();
- if ((table.getStyle() & SWT.CHECK) == 0) return;
- if (tableItem != null) {
- tableItem.setGrayed(grayed);
- }
- this.grayed = grayed;
-}
-
-/**
- * Sets the expanded state.
- * <p>
- * @param expanded the new expanded state.
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * </ul>
- */
-public void setExpanded (boolean expanded) {
- checkWidget();
- if (items.length == 0) return;
- this.expanded = expanded;
- if (tableItem == null) return;
- parent.setRedraw(false);
- for (int i = 0; i < items.length; i++) {
- items[i].setVisible(expanded);
- }
- Image image = expanded ? parent.getMinusImage() : parent.getPlusImage();
- tableItem.setImage(0, image);
- parent.setRedraw(true);
-}
-
-/**
- * Sets the receiver's foreground color to the color specified
- * by the argument, or to the default system color for the item
- * if the argument is null.
- *
- * @param color the new color (or null)
- *
- * @since 2.0
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- *
- * @since 2.0
- *
- */
-public void setForeground (Color color) {
- checkWidget ();
- if (color != null && color.isDisposed ()) {
- SWT.error (SWT.ERROR_INVALID_ARGUMENT);
- }
- if (tableItem != null) {
- tableItem.setForeground(color);
- }
- foreground = color;
-}
-
-/**
- * Sets the image at an index.
- * <p>
- * The image can be null.
- * The image in column 0 is reserved for the [+] and [-]
- * images of the tree, therefore do nothing if index is 0.
- *
- * @param image the new image or null
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * </ul>
- */
-public void setImage (int index, Image image) {
- checkWidget();
- int columnCount = Math.max(parent.getTable().getColumnCount(), 1);
- if (index <= 0 || index >= columnCount) return;
- if (images.length < columnCount) {
- Image[] newImages = new Image[columnCount];
- System.arraycopy(images, 0, newImages, 0, images.length);
- images = newImages;
- }
- images[index] = image;
- if (tableItem != null) tableItem.setImage(index, image);
-}
-
-/**
- * Sets the first image.
- * <p>
- * The image can be null.
- * The image in column 0 is reserved for the [+] and [-]
- * images of the tree, therefore do nothing.
- *
- * @param image the new image or null
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_NULL_ARGUMENT when string is null</li>
- * </ul>
- */
-public void setImage (Image image) {
- setImage(0, image);
-}
-
-/**
- * Sets the widget text.
- * <p>
- *
- * The widget text for an item is the label of the
- * item or the label of the text specified by a column
- * number.
- *
- * @param index the column number
- * @param text the new text
- *
- * @exception SWTError <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
- * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
- * <li>ERROR_NULL_ARGUMENT when string is null</li>
- * </ul>
- */
-public void setText(int index, String text) {
- checkWidget();
- int columnCount = Math.max(parent.getTable().getColumnCount(), 1);
- if (index < 0 || index >= columnCount) return;
- if (texts.length < columnCount) {
- String[] newTexts = new String[columnCount];
- System.arraycopy(texts, 0, newTexts, 0, texts.length);
- texts = newTexts;
- }
- texts[index] = text;
- if (tableItem != null) tableItem.setText(index, text);
-}
-public void setText (String string) {
- setText(0, string);
-}
-
-void setVisible (boolean show) {
- if (parentItem == null) return; // this is a root and can not be toggled between visible and hidden
- if (getVisible() == show) return;
-
- if (show) {
- if (!parentItem.getVisible()) return; // parentItem must already be visible
- // create underlying table item and set data in table item to stored data
- Table table = parent.getTable();
- int parentIndex = table.indexOf(parentItem.tableItem);
- int index = parentItem.expandedIndexOf(this) + parentIndex + 1;
- if (index < 0) return;
- tableItem = new TableItem(table, getStyle(), index);
- tableItem.setData(TableTree.ITEMID, this);
- tableItem.setImageIndent(getIndent());
- if (background != null) tableItem.setBackground(background);
- if (foreground != null) tableItem.setForeground(foreground);
- addCheck();
-
- // restore fields to item
- // ignore any images in the first column
- int columnCount = Math.max(table.getColumnCount(), 1);
- for (int i = 0; i < columnCount; i++) {
- if (i < texts.length && texts[i] != null) setText(i, texts[i]);
- if (i < images.length && images[i] != null) setImage(i, images[i]);
- }
-
- // display the children and the appropriate [+]/[-] symbol as required
- if (items.length != 0) {
- if (expanded) {
- tableItem.setImage(0, parent.getMinusImage());
- for (int i = 0, length = items.length; i < length; i++) {
- items[i].setVisible(true);
- }
- } else {
- tableItem.setImage(0, parent.getPlusImage());
- }
- }
-
- } else {
-
- for (int i = 0, length = items.length; i < length; i++) {
- items[i].setVisible(false);
- }
- // remove row from table
- tableItem.dispose();
- tableItem = null;
- }
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A TableTreeItem is a selectable user interface object
+ * that represents an item in a heirarchy of items in a
+ * TableTree.
+ */
+public class TableTreeItem extends Item {
+ TableItem tableItem;
+ TableTree parent;
+ TableTreeItem parentItem;
+ TableTreeItem [] items = TableTree.EMPTY_ITEMS;
+ String[] texts = TableTree.EMPTY_TEXTS;
+ Image[] images = TableTree.EMPTY_IMAGES;
+ Color background;
+ Color foreground;
+ boolean expanded;
+ boolean checked;
+ boolean grayed;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>TableTree</code>)
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#getStyle
+ */
+public TableTreeItem(TableTree parent, int style) {
+ this (parent, style, parent.getItemCount());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>TableTree</code>,
+ * a style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#getStyle
+ */
+public TableTreeItem(TableTree parent, int style, int index) {
+ this (parent, null, style, index);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>TableTreeItem</code>)
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parentItem a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#getStyle
+ */
+public TableTreeItem(TableTreeItem parent, int style) {
+ this (parent, style, parent.getItemCount());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>TableTreeItem</code>),
+ * a style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parentItem a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#getStyle
+ */
+public TableTreeItem(TableTreeItem parent, int style, int index) {
+ this (parent.getParent(), parent, style, index);
+}
+
+TableTreeItem(TableTree parent, TableTreeItem parentItem, int style, int index) {
+ super(parent, style);
+ this.parent = parent;
+ this.parentItem = parentItem;
+ if (parentItem == null) {
+
+ /* Root items are visible immediately */
+ int tableIndex = parent.addItem(this, index);
+ tableItem = new TableItem(parent.getTable(), style, tableIndex);
+ tableItem.setData(TableTree.ITEMID, this);
+ addCheck();
+ /*
+ * Feature in the Table. The table uses the first image that
+ * is inserted into the table to size the table rows. If the
+ * user is allowed to insert the first image, this will cause
+ * the +/- images to be scaled. The fix is to insert a dummy
+ * image to force the size.
+ */
+ if (parent.sizeImage == null) {
+ int itemHeight = parent.getItemHeight();
+ parent.sizeImage = new Image(null, itemHeight, itemHeight);
+ GC gc = new GC (parent.sizeImage);
+ gc.setBackground(parent.getBackground());
+ gc.fillRectangle(0, 0, itemHeight, itemHeight);
+ gc.dispose();
+ tableItem.setImage(0, parent.sizeImage);
+ }
+ } else {
+ parentItem.addItem(this, index);
+ }
+}
+void addCheck() {
+ Table table = parent.getTable();
+ if ((table.getStyle() & SWT.CHECK) == 0) return;
+ tableItem.setChecked(checked);
+ tableItem.setGrayed(grayed);
+}
+void addItem(TableTreeItem item, int index) {
+ if (item == null) throw new SWTError(SWT.ERROR_NULL_ARGUMENT);
+ if (index < 0 || index > items.length) throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);
+
+ /* Now that item has a sub-node it must indicate that it can be expanded */
+ if (items.length == 0 && index == 0) {
+ if (tableItem != null) {
+ Image image = expanded ? parent.getMinusImage() : parent.getPlusImage();
+ tableItem.setImage(0, image);
+ }
+ }
+
+ /* Put the item in the items list */
+ TableTreeItem[] newItems = new TableTreeItem[items.length + 1];
+ System.arraycopy(items, 0, newItems, 0, index);
+ newItems[index] = item;
+ System.arraycopy(items, index, newItems, index + 1, items.length - index);
+ items = newItems;
+ if (expanded) item.setVisible(true);
+}
+
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public Color getBackground () {
+ checkWidget ();
+ return (background == null) ? parent.getBackground() : background;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds (int index) {
+ checkWidget();
+ if (tableItem != null) {
+ return tableItem.getBounds(index);
+ } else {
+ return new Rectangle(0, 0, 0, 0);
+ }
+}
+/**
+ * Returns <code>true</code> if the receiver is checked,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK style, return false.
+ *
+ * @return the checked state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getChecked () {
+ checkWidget();
+ if (tableItem == null) return checked;
+ return tableItem.getChecked();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is grayed,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK</code> style, return false.
+ *
+ * @return the grayed state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public boolean getGrayed () {
+ checkWidget();
+ if (tableItem == null) return grayed;
+ return tableItem.getGrayed();
+}
+
+public Display getDisplay () {
+ TableTree parent = this.parent;
+ if (parent == null) throw new SWTError (SWT.ERROR_WIDGET_DISPOSED);
+ return parent.getDisplay ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is expanded,
+ * and false otherwise.
+ * <p>
+ *
+ * @return the expanded state
+ */
+public boolean getExpanded () {
+ //checkWidget();
+ return expanded;
+}
+
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public Color getForeground () {
+ checkWidget ();
+ return (foreground == null) ? parent.getForeground() : foreground;
+}
+/**
+ * Gets the first image.
+ * <p>
+ * The image in column 0 is reserved for the [+] and [-]
+ * images of the tree, therefore getImage(0) will return null.
+ *
+ * @return the image at index 0
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage () {
+ checkWidget();
+ return getImage(0);
+}
+
+/**
+ * Gets the image at the specified index.
+ * <p>
+ * Indexing is zero based. The image can be null.
+ * The image in column 0 is reserved for the [+] and [-]
+ * images of the tree, therefore getImage(0) will return null.
+ * Return null if the index is out of range.
+ *
+ * @param index the index of the image
+ * @return the image at the specified index or null
+ */
+public Image getImage (int index) {
+ //checkWidget();
+ if (0 < index && index < images.length) return images[index];
+ return null;
+}
+
+int getIndent() {
+ if (parentItem == null) return 0;
+ return parentItem.getIndent() + 1;
+}
+
+/**
+ * Returns the number of items contained in the receiver
+ * that are direct item children of the receiver.
+ *
+ * @return the number of items
+ */
+public int getItemCount () {
+ //checkWidget();
+ return items.length;
+}
+
+/**
+ * Returns an array of <code>TableTreeItem</code>s which are the
+ * direct item children of the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the receiver's items
+ */
+public TableTreeItem[] getItems () {
+ //checkWidget();
+ TableTreeItem[] newItems = new TableTreeItem[items.length];
+ System.arraycopy(items, 0, newItems, 0, items.length);
+ return newItems;
+}
+
+TableTreeItem getItem(TableItem tableItem) {
+ if (tableItem == null) return null;
+ if (this.tableItem == tableItem) return this;
+ for (int i = 0; i < items.length; i++) {
+ TableTreeItem item = items[i].getItem(tableItem);
+ if (item != null) return item;
+ }
+ return null;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>TableTree</code>.
+ *
+ * @return the receiver's parent
+ */
+public TableTree getParent () {
+ //checkWidget();
+ return parent;
+}
+
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>TableTreeItem</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ */
+public TableTreeItem getParentItem () {
+ //checkWidget();
+ return parentItem;
+}
+public String getText () {
+ checkWidget();
+ return getText(0);
+}
+
+/**
+ * Gets the item text at the specified index.
+ * <p>
+ * Indexing is zero based.
+ *
+ * This operation will fail when the index is out
+ * of range or an item could not be queried from
+ * the OS.
+ *
+ * @param index the index of the item
+ * @return the item text at the specified index, which can be null
+ */
+public String getText(int index) {
+ //checkWidget();
+ if (0 <= index && index < texts.length) return texts[index];
+ return null;
+}
+
+boolean getVisible () {
+ return tableItem != null;
+}
+
+/**
+ * Gets the index of the specified item.
+ *
+ * <p>The widget is searched starting at 0 until an
+ * item is found that is equal to the search item.
+ * If no item is found, -1 is returned. Indexing
+ * is zero based. This index is relative to the parent only.
+ *
+ * @param item the search item
+ * @return the index of the item or -1 if the item is not found
+ *
+ */
+public int indexOf (TableTreeItem item) {
+ //checkWidget();
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] == item) return i;
+ }
+ return -1;
+}
+
+void expandAll(boolean notify) {
+ if (items.length == 0) return;
+ if (!expanded) {
+ setExpanded(true);
+ if (notify) {
+ Event event = new Event();
+ event.item = this;
+ parent.notifyListeners(SWT.Expand, event);
+ }
+ }
+ for (int i = 0; i < items.length; i++) {
+ items[i].expandAll(notify);
+ }
+}
+int expandedIndexOf (TableTreeItem item) {
+ int index = 0;
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] == item) return index;
+ if (items[i].expanded) index += items[i].visibleChildrenCount ();
+ index++;
+ }
+ return -1;
+}
+
+int visibleChildrenCount () {
+ int count = 0;
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].getVisible ()) {
+ count += 1 + items[i].visibleChildrenCount ();
+ }
+ }
+ return count;
+}
+
+public void dispose () {
+ if (isDisposed()) return;
+ for (int i = items.length - 1; i >= 0; i--) {
+ items[i].dispose();
+ }
+ super.dispose();
+ if (!parent.inDispose) {
+ if (parentItem != null) {
+ parentItem.removeItem(this);
+ } else {
+ parent.removeItem(this);
+ }
+ if (tableItem != null) tableItem.dispose();
+ }
+ items = null;
+ parentItem = null;
+ parent = null;
+ images = null;
+ texts = null;
+ tableItem = null;
+ foreground = null;
+ background = null;
+}
+
+void removeItem(TableTreeItem item) {
+ int index = 0;
+ while (index < items.length && items[index] != item) index++;
+ if (index == items.length) return;
+ TableTreeItem[] newItems = new TableTreeItem[items.length - 1];
+ System.arraycopy(items, 0, newItems, 0, index);
+ System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
+ items = newItems;
+ if (items.length == 0) {
+ if (tableItem != null) tableItem.setImage(0, null);
+ }
+}
+
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public void setBackground (Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (tableItem != null) {
+ tableItem.setBackground(color);
+ }
+ background = color;
+}
+
+/**
+ * Sets the checked state of the checkbox for this item. This state change
+ * only applies if the Table was created with the SWT.CHECK style.
+ *
+ * @param checked the new checked state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setChecked (boolean checked) {
+ checkWidget();
+ Table table = parent.getTable();
+ if ((table.getStyle() & SWT.CHECK) == 0) return;
+ if (tableItem != null) {
+ tableItem.setChecked(checked);
+ }
+ this.checked = checked;
+}
+
+/**
+ * Sets the grayed state of the checkbox for this item. This state change
+ * only applies if the Table was created with the SWT.CHECK style.
+ *
+ * @param grayed the new grayed state of the checkbox;
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void setGrayed (boolean grayed) {
+ checkWidget();
+ Table table = parent.getTable();
+ if ((table.getStyle() & SWT.CHECK) == 0) return;
+ if (tableItem != null) {
+ tableItem.setGrayed(grayed);
+ }
+ this.grayed = grayed;
+}
+
+/**
+ * Sets the expanded state.
+ * <p>
+ * @param expanded the new expanded state.
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * </ul>
+ */
+public void setExpanded (boolean expanded) {
+ checkWidget();
+ if (items.length == 0) return;
+ this.expanded = expanded;
+ if (tableItem == null) return;
+ parent.setRedraw(false);
+ for (int i = 0; i < items.length; i++) {
+ items[i].setVisible(expanded);
+ }
+ Image image = expanded ? parent.getMinusImage() : parent.getPlusImage();
+ tableItem.setImage(0, image);
+ parent.setRedraw(true);
+}
+
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @since 2.0
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public void setForeground (Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (tableItem != null) {
+ tableItem.setForeground(color);
+ }
+ foreground = color;
+}
+
+/**
+ * Sets the image at an index.
+ * <p>
+ * The image can be null.
+ * The image in column 0 is reserved for the [+] and [-]
+ * images of the tree, therefore do nothing if index is 0.
+ *
+ * @param image the new image or null
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * </ul>
+ */
+public void setImage (int index, Image image) {
+ checkWidget();
+ int columnCount = Math.max(parent.getTable().getColumnCount(), 1);
+ if (index <= 0 || index >= columnCount) return;
+ if (images.length < columnCount) {
+ Image[] newImages = new Image[columnCount];
+ System.arraycopy(images, 0, newImages, 0, images.length);
+ images = newImages;
+ }
+ images[index] = image;
+ if (tableItem != null) tableItem.setImage(index, image);
+}
+
+/**
+ * Sets the first image.
+ * <p>
+ * The image can be null.
+ * The image in column 0 is reserved for the [+] and [-]
+ * images of the tree, therefore do nothing.
+ *
+ * @param image the new image or null
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_NULL_ARGUMENT when string is null</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+ setImage(0, image);
+}
+
+/**
+ * Sets the widget text.
+ * <p>
+ *
+ * The widget text for an item is the label of the
+ * item or the label of the text specified by a column
+ * number.
+ *
+ * @param index the column number
+ * @param text the new text
+ *
+ * @exception SWTError <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
+ * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
+ * <li>ERROR_NULL_ARGUMENT when string is null</li>
+ * </ul>
+ */
+public void setText(int index, String text) {
+ checkWidget();
+ int columnCount = Math.max(parent.getTable().getColumnCount(), 1);
+ if (index < 0 || index >= columnCount) return;
+ if (texts.length < columnCount) {
+ String[] newTexts = new String[columnCount];
+ System.arraycopy(texts, 0, newTexts, 0, texts.length);
+ texts = newTexts;
+ }
+ texts[index] = text;
+ if (tableItem != null) tableItem.setText(index, text);
+}
+public void setText (String string) {
+ setText(0, string);
+}
+
+void setVisible (boolean show) {
+ if (parentItem == null) return; // this is a root and can not be toggled between visible and hidden
+ if (getVisible() == show) return;
+
+ if (show) {
+ if (!parentItem.getVisible()) return; // parentItem must already be visible
+ // create underlying table item and set data in table item to stored data
+ Table table = parent.getTable();
+ int parentIndex = table.indexOf(parentItem.tableItem);
+ int index = parentItem.expandedIndexOf(this) + parentIndex + 1;
+ if (index < 0) return;
+ tableItem = new TableItem(table, getStyle(), index);
+ tableItem.setData(TableTree.ITEMID, this);
+ tableItem.setImageIndent(getIndent());
+ if (background != null) tableItem.setBackground(background);
+ if (foreground != null) tableItem.setForeground(foreground);
+ addCheck();
+
+ // restore fields to item
+ // ignore any images in the first column
+ int columnCount = Math.max(table.getColumnCount(), 1);
+ for (int i = 0; i < columnCount; i++) {
+ if (i < texts.length && texts[i] != null) setText(i, texts[i]);
+ if (i < images.length && images[i] != null) setImage(i, images[i]);
+ }
+
+ // display the children and the appropriate [+]/[-] symbol as required
+ if (items.length != 0) {
+ if (expanded) {
+ tableItem.setImage(0, parent.getMinusImage());
+ for (int i = 0, length = items.length; i < length; i++) {
+ items[i].setVisible(true);
+ }
+ } else {
+ tableItem.setImage(0, parent.getPlusImage());
+ }
+ }
+
+ } else {
+
+ for (int i = 0, length = items.length; i < length; i++) {
+ items[i].setVisible(false);
+ }
+ // remove row from table
+ tableItem.dispose();
+ tableItem = null;
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangeListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangeListener.java
index d3efd17bba..682da982db 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangeListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangeListener.java
@@ -1,57 +1,57 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.internal.SWTEventListener;
-
-/**
- * The StyledText widget implements this listener to receive
- * notifications when changes to the model occur.
- * It is not intended to be implemented by clients or by
- * implementors of StyledTextContent.
- * Clients should listen to the ModifyEvent or ExtendedModifyEvent
- * that is sent by the StyledText widget to receive text change
- * notifications.
- * Implementors of StyledTextContent should call the textChanging
- * and textChanged methods when text changes occur as described
- * below. If the entire text is replaced the textSet method
- * should be called instead.
- */
-public interface TextChangeListener extends SWTEventListener {
-
-/**
- * This method is called when the content is about to be changed.
- * Callers also need to call the textChanged method after the
- * content change has been applied. The widget only updates the
- * screen properly when it receives both events.
- *
- * @param event the text changing event. All event fields need
- * to be set by the sender.
- * @see TextChangingEvent
- */
-public void textChanging(TextChangingEvent event);
-/**
- * This method is called when the content has changed.
- * Callers need to have called the textChanging method prior to
- * applying the content change and calling this method. The widget
- * only updates the screen properly when it receives both events.
- *
- * @param event the text changed event
- */
-public void textChanged(TextChangedEvent event);
-/**
- * This method is called instead of the textChanging/textChanged
- * combination when the entire old content has been replaced
- * (e.g., by a call to StyledTextContent.setText()).
- *
- * @param event the text changed event
- */
-public void textSet(TextChangedEvent event);
-}
-
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.internal.SWTEventListener;
+
+/**
+ * The StyledText widget implements this listener to receive
+ * notifications when changes to the model occur.
+ * It is not intended to be implemented by clients or by
+ * implementors of StyledTextContent.
+ * Clients should listen to the ModifyEvent or ExtendedModifyEvent
+ * that is sent by the StyledText widget to receive text change
+ * notifications.
+ * Implementors of StyledTextContent should call the textChanging
+ * and textChanged methods when text changes occur as described
+ * below. If the entire text is replaced the textSet method
+ * should be called instead.
+ */
+public interface TextChangeListener extends SWTEventListener {
+
+/**
+ * This method is called when the content is about to be changed.
+ * Callers also need to call the textChanged method after the
+ * content change has been applied. The widget only updates the
+ * screen properly when it receives both events.
+ *
+ * @param event the text changing event. All event fields need
+ * to be set by the sender.
+ * @see TextChangingEvent
+ */
+public void textChanging(TextChangingEvent event);
+/**
+ * This method is called when the content has changed.
+ * Callers need to have called the textChanging method prior to
+ * applying the content change and calling this method. The widget
+ * only updates the screen properly when it receives both events.
+ *
+ * @param event the text changed event
+ */
+public void textChanged(TextChangedEvent event);
+/**
+ * This method is called instead of the textChanging/textChanged
+ * combination when the entire old content has been replaced
+ * (e.g., by a call to StyledTextContent.setText()).
+ *
+ * @param event the text changed event
+ */
+public void textSet(TextChangedEvent event);
+}
+
+
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangedEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangedEvent.java
index b76b0cc42a..5c4749959c 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangedEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangedEvent.java
@@ -1,26 +1,26 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-
-/**
- * This event is sent by the StyledTextContent implementor when a change to
- * the text occurs.
- */
-public class TextChangedEvent extends TypedEvent {
-/**
- * Create the TextChangedEvent to be used by the StyledTextContent implementor.
- * <p>
- *
- * @param source the object that will be sending the TextChangedEvent,
- * cannot be null
- */
-public TextChangedEvent(StyledTextContent source) {
- super(source);
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+
+/**
+ * This event is sent by the StyledTextContent implementor when a change to
+ * the text occurs.
+ */
+public class TextChangedEvent extends TypedEvent {
+/**
+ * Create the TextChangedEvent to be used by the StyledTextContent implementor.
+ * <p>
+ *
+ * @param source the object that will be sending the TextChangedEvent,
+ * cannot be null
+ */
+public TextChangedEvent(StyledTextContent source) {
+ super(source);
+}
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangingEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangingEvent.java
index 19ac181426..0668a05f64 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangingEvent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TextChangingEvent.java
@@ -1,63 +1,63 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-
-/**
- * This event is sent by the StyledTextContent implementor when a change
- * to the text is about to occur.
- */
-public class TextChangingEvent extends TypedEvent {
- /**
- * Start offset of the text that is going to be replaced
- */
- public int start;
- /**
- * Text that is going to be inserted or empty string
- * if no text will be inserted
- */
- public String newText;
- /**
- * Length of text that is going to be replaced
- */
- public int replaceCharCount;
- /**
- * Length of text that is going to be inserted
- */
- public int newCharCount;
- /**
- * Number of lines that are going to be replaced
- */
- public int replaceLineCount;
- /**
- * Number of new lines that are going to be inserted
- */
- public int newLineCount;
-
-/**
- * Create the TextChangedEvent to be used by the StyledTextContent implementor.
- * <p>
- *
- * @param source the object that will be sending the new TextChangingEvent,
- * cannot be null
- */
-public TextChangingEvent(StyledTextContent source) {
- super(source);
-}
-TextChangingEvent(StyledTextContent source, StyledTextEvent e) {
- super(source);
- start = e.start;
- replaceCharCount = e.replaceCharCount;
- newCharCount = e.newCharCount;
- replaceLineCount = e.replaceLineCount;
- newLineCount = e.newLineCount;
- newText = e.text;
-}
-
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+
+/**
+ * This event is sent by the StyledTextContent implementor when a change
+ * to the text is about to occur.
+ */
+public class TextChangingEvent extends TypedEvent {
+ /**
+ * Start offset of the text that is going to be replaced
+ */
+ public int start;
+ /**
+ * Text that is going to be inserted or empty string
+ * if no text will be inserted
+ */
+ public String newText;
+ /**
+ * Length of text that is going to be replaced
+ */
+ public int replaceCharCount;
+ /**
+ * Length of text that is going to be inserted
+ */
+ public int newCharCount;
+ /**
+ * Number of lines that are going to be replaced
+ */
+ public int replaceLineCount;
+ /**
+ * Number of new lines that are going to be inserted
+ */
+ public int newLineCount;
+
+/**
+ * Create the TextChangedEvent to be used by the StyledTextContent implementor.
+ * <p>
+ *
+ * @param source the object that will be sending the new TextChangingEvent,
+ * cannot be null
+ */
+public TextChangingEvent(StyledTextContent source) {
+ super(source);
+}
+TextChangingEvent(StyledTextContent source, StyledTextEvent e) {
+ super(source);
+ start = e.start;
+ replaceCharCount = e.replaceCharCount;
+ newCharCount = e.newCharCount;
+ replaceLineCount = e.replaceLineCount;
+ newLineCount = e.newLineCount;
+ newText = e.text;
+}
+
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TreeEditor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TreeEditor.java
index 09916eae1b..d3b80b447c 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TreeEditor.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/TreeEditor.java
@@ -1,162 +1,162 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.events.*;
-
-/**
-*
-* A TreeEditor is a manager for a Control that appears above a cell in a Tree and tracks with the
-* moving and resizing of that cell. It can be used to display a text widget above a cell
-* in a Tree so that the user can edit the contents of that cell. It can also be used to display
-* a button that can launch a dialog for modifying the contents of the associated cell.
-*
-* <p> Here is an example of using a TreeEditor:
-* <code><pre>
-* final Tree tree = new Tree(parent, SWT.FULL_SELECTION);
-* final TreeEditor editor = new TreeEditor (tree);
-* tree.addSelectionListener (new SelectionAdapter() {
-* public void widgetSelected(SelectionEvent e) {
-*
-* // Clean up any previous editor control
-* Control oldEditor = editor.getEditor();
-* if (oldEditor != null)
-* oldEditor.dispose();
-*
-* // Identify the selected row
-* TreeItem item = (TreeItem)e.item;
-*
-* // The control that will be the editor must be a child of the Tree
-* Text text = new Text(tree, SWT.NONE);
-*
-* //The text editor must have the same size as the cell and must
-* //not be any smaller than 50 pixels.
-* editor.horizontalAlignment = SWT.LEFT;
-* editor.grabHorizontal = true;
-* editor.minimumWidth = 50;
-*
-* // Open the text editor on the selected row.
-* editor.setEditor (text, item);
-*
-* // Assign focus to the text control
-* text.setFocus ();
-* }
-* });
-* </pre></code>
-*/
-public class TreeEditor extends ControlEditor {
- Tree tree;
- TreeItem item;
- TreeListener treeListener;
-/**
-* Creates a TreeEditor for the specified Tree.
-*
-* @param tree the Tree Control above which this editor will be displayed
-*
-*/
-public TreeEditor (Tree tree) {
- super(tree);
- this.tree = tree;
- treeListener = new TreeAdapter () {
- final Runnable runnable = new Runnable() {
- public void run() {
- if (TreeEditor.this.tree.isDisposed() || editor == null) return;
- resize();
- editor.setVisible(true);
- }
- };
- public void treeCollapsed(TreeEvent e) {
- if (editor == null) return;
- editor.setVisible(false);
- Display display = TreeEditor.this.tree.getDisplay();
- display.asyncExec(runnable);
- }
- public void treeExpanded(TreeEvent e) {
- if (editor == null) return;
- editor.setVisible(false);
- Display display = TreeEditor.this.tree.getDisplay();
- display.asyncExec(runnable);
- }
- };
- tree.addTreeListener(treeListener);
-}
-Rectangle computeBounds () {
- if (item == null || item.isDisposed()) return new Rectangle(0, 0, 0, 0);
-
- Rectangle cell = item.getBounds();
- Rectangle area = tree.getClientArea();
- if (cell.x < area.x + area.width) {
- if (cell.x + cell.width > area.x + area.width) {
- cell.width = area.x + area.width - cell.x;
- }
- }
- Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, cell.height);
-
- if (grabHorizontal) {
- editorRect.width = Math.max(area.x + area.width - cell.x, minimumWidth);
- }
-
- if (horizontalAlignment == SWT.RIGHT) {
- editorRect.x = Math.max(cell.x, cell.x + cell.width - editorRect.width);
- } else if (horizontalAlignment == SWT.LEFT) {
- // do nothing - cell.x is the right answer
- } else { // default is CENTER
- editorRect.x = Math.max(cell.x, cell.x + (cell.width - editorRect.width)/2);
- }
-
- return editorRect;
-}
-/**
- * Removes all associations between the TreeEditor and the cell in the tree. The
- * tree and the editor Control are <b>not</b> disposed.
- */
-public void dispose () {
- if (treeListener != null)
- tree.removeTreeListener(treeListener);
- treeListener = null;
- tree = null;
- item = null;
- super.dispose();
-}
-/**
-* Returns the TreeItem for the row of the cell being tracked by this editor.
-*
-* @return the TreeItem for the row of the cell being tracked by this editor
-*/
-public TreeItem getItem () {
- return item;
-}
-public void setItem (TreeItem item) {
- this.item = item;
- resize();
-}
-
-/**
-* Specify the Control that is to be displayed and the cell in the tree that it is to be positioned above.
-*
-* <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Tree control
-* specified in the TreeEditor constructor.
-*
-* @param editor the Control that is displayed above the cell being edited
-* @param item the TreeItem for the row of the cell being tracked by this editor
-* @param column the zero based index of the column of the cell being tracked by this editor
-*/
-public void setEditor (Control editor, TreeItem item) {
- setItem (item);
- super.setEditor (editor);
-}
-
-void resize () {
- if (tree.isDisposed()) return;
- if (item == null || item.isDisposed()) return;
- super.resize();
-}
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.events.*;
+
+/**
+*
+* A TreeEditor is a manager for a Control that appears above a cell in a Tree and tracks with the
+* moving and resizing of that cell. It can be used to display a text widget above a cell
+* in a Tree so that the user can edit the contents of that cell. It can also be used to display
+* a button that can launch a dialog for modifying the contents of the associated cell.
+*
+* <p> Here is an example of using a TreeEditor:
+* <code><pre>
+* final Tree tree = new Tree(parent, SWT.FULL_SELECTION);
+* final TreeEditor editor = new TreeEditor (tree);
+* tree.addSelectionListener (new SelectionAdapter() {
+* public void widgetSelected(SelectionEvent e) {
+*
+* // Clean up any previous editor control
+* Control oldEditor = editor.getEditor();
+* if (oldEditor != null)
+* oldEditor.dispose();
+*
+* // Identify the selected row
+* TreeItem item = (TreeItem)e.item;
+*
+* // The control that will be the editor must be a child of the Tree
+* Text text = new Text(tree, SWT.NONE);
+*
+* //The text editor must have the same size as the cell and must
+* //not be any smaller than 50 pixels.
+* editor.horizontalAlignment = SWT.LEFT;
+* editor.grabHorizontal = true;
+* editor.minimumWidth = 50;
+*
+* // Open the text editor on the selected row.
+* editor.setEditor (text, item);
+*
+* // Assign focus to the text control
+* text.setFocus ();
+* }
+* });
+* </pre></code>
+*/
+public class TreeEditor extends ControlEditor {
+ Tree tree;
+ TreeItem item;
+ TreeListener treeListener;
+/**
+* Creates a TreeEditor for the specified Tree.
+*
+* @param tree the Tree Control above which this editor will be displayed
+*
+*/
+public TreeEditor (Tree tree) {
+ super(tree);
+ this.tree = tree;
+ treeListener = new TreeAdapter () {
+ final Runnable runnable = new Runnable() {
+ public void run() {
+ if (TreeEditor.this.tree.isDisposed() || editor == null) return;
+ resize();
+ editor.setVisible(true);
+ }
+ };
+ public void treeCollapsed(TreeEvent e) {
+ if (editor == null) return;
+ editor.setVisible(false);
+ Display display = TreeEditor.this.tree.getDisplay();
+ display.asyncExec(runnable);
+ }
+ public void treeExpanded(TreeEvent e) {
+ if (editor == null) return;
+ editor.setVisible(false);
+ Display display = TreeEditor.this.tree.getDisplay();
+ display.asyncExec(runnable);
+ }
+ };
+ tree.addTreeListener(treeListener);
+}
+Rectangle computeBounds () {
+ if (item == null || item.isDisposed()) return new Rectangle(0, 0, 0, 0);
+
+ Rectangle cell = item.getBounds();
+ Rectangle area = tree.getClientArea();
+ if (cell.x < area.x + area.width) {
+ if (cell.x + cell.width > area.x + area.width) {
+ cell.width = area.x + area.width - cell.x;
+ }
+ }
+ Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, cell.height);
+
+ if (grabHorizontal) {
+ editorRect.width = Math.max(area.x + area.width - cell.x, minimumWidth);
+ }
+
+ if (horizontalAlignment == SWT.RIGHT) {
+ editorRect.x = Math.max(cell.x, cell.x + cell.width - editorRect.width);
+ } else if (horizontalAlignment == SWT.LEFT) {
+ // do nothing - cell.x is the right answer
+ } else { // default is CENTER
+ editorRect.x = Math.max(cell.x, cell.x + (cell.width - editorRect.width)/2);
+ }
+
+ return editorRect;
+}
+/**
+ * Removes all associations between the TreeEditor and the cell in the tree. The
+ * tree and the editor Control are <b>not</b> disposed.
+ */
+public void dispose () {
+ if (treeListener != null)
+ tree.removeTreeListener(treeListener);
+ treeListener = null;
+ tree = null;
+ item = null;
+ super.dispose();
+}
+/**
+* Returns the TreeItem for the row of the cell being tracked by this editor.
+*
+* @return the TreeItem for the row of the cell being tracked by this editor
+*/
+public TreeItem getItem () {
+ return item;
+}
+public void setItem (TreeItem item) {
+ this.item = item;
+ resize();
+}
+
+/**
+* Specify the Control that is to be displayed and the cell in the tree that it is to be positioned above.
+*
+* <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Tree control
+* specified in the TreeEditor constructor.
+*
+* @param editor the Control that is displayed above the cell being edited
+* @param item the TreeItem for the row of the cell being tracked by this editor
+* @param column the zero based index of the column of the cell being tracked by this editor
+*/
+public void setEditor (Control editor, TreeItem item) {
+ setItem (item);
+ super.setEditor (editor);
+}
+
+void resize () {
+ if (tree.isDisposed()) return;
+ if (item == null || item.isDisposed()) return;
+ super.resize();
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/VerifyKeyListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/VerifyKeyListener.java
index 1af445df29..151861bbb3 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/VerifyKeyListener.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/VerifyKeyListener.java
@@ -1,20 +1,20 @@
-package org.eclipse.swt.custom;
-/*
+package org.eclipse.swt.custom;
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.events.*;
-import org.eclipse.swt.internal.SWTEventListener;
-
-public interface VerifyKeyListener extends SWTEventListener {
-/**
- * @param event.character the character that was typed (input)
- * @param event.keyCode the key code that was typed (input)
- * @param event.stateMask the state of the keyboard (input)
- * @param event.doit processed or not (output)
- */
-public void verifyKey (VerifyEvent event);
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.internal.SWTEventListener;
+
+public interface VerifyKeyListener extends SWTEventListener {
+/**
+ * @param event.character the character that was typed (input)
+ * @param event.keyCode the key code that was typed (input)
+ * @param event.stateMask the state of the keyboard (input)
+ * @param event.doit processed or not (output)
+ */
+public void verifyKey (VerifyEvent event);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ViewForm.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ViewForm.java
index 82da8294d5..675aa437dc 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ViewForm.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/ViewForm.java
@@ -1,614 +1,614 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.widgets.*;
-import org.eclipse.swt.*;
-import org.eclipse.swt.events.*;
-
-/**
- * Instances of this class implement a Composite that lays out three
- * children horizontally and allows programmatic control of layout and
- * border parameters. ViewForm is used in the workbench to implement a
- * view's label/menu/toolbar local bar.
- * <p>
- * Note that although this class is a subclass of <code>Composite</code>,
- * it does not make sense to set a layout on it.
- * </p><p>
- * <dl>
- * <dt><b>Styles:</b></dt>
- * <dd>BORDER, FLAT</dd>
- * <dt><b>Events:</b></dt>
- * <dd>(None)</dd>
- * </dl>
- * <p>
- * IMPORTANT: This class is <em>not</em> intended to be subclassed.
- * </p>
- */
-
-public class ViewForm extends Composite {
-
- /**
- * marginWidth specifies the number of pixels of horizontal margin
- * that will be placed along the left and right edges of the form.
- *
- * The default value is 0.
- */
- public int marginWidth = 0;
- /**
- * marginHeight specifies the number of pixels of vertical margin
- * that will be placed along the top and bottom edges of the form.
- *
- * The default value is 0.
- */
- public int marginHeight = 0;
-
- /**
- * Color of innermost line of drop shadow border.
- */
- public static RGB borderInsideRGB = new RGB (132, 130, 132);
- /**
- * Color of middle line of drop shadow border.
- */
- public static RGB borderMiddleRGB = new RGB (143, 141, 138);
- /**
- * Color of outermost line of drop shadow border.
- */
- public static RGB borderOutsideRGB = new RGB (171, 168, 165);
-
- // SWT widgets
- private Control topLeft;
- private Control topCenter;
- private Control topRight;
- private Control content;
-
- // Configuration and state info
- private boolean separateTopCenter = false;
- private int drawLine1 = -1;
- private int drawLine2 = -1;
-
- private boolean showBorder = false;
-
- private int BORDER_TOP = 0;
- private int BORDER_BOTTOM = 0;
- private int BORDER_LEFT = 0;
- private int BORDER_RIGHT = 0;
-
- private Color borderColor1;
- private Color borderColor2;
- private Color borderColor3;
-
- private Rectangle oldArea;
- private static final int OFFSCREEN = -200;
-/**
- * Constructs a new instance of this class given its parent
- * and a style value describing its behavior and appearance.
- * <p>
- * The style value is either one of the style constants defined in
- * class <code>SWT</code> which is applicable to instances of this
- * class, or must be built by <em>bitwise OR</em>'ing together
- * (that is, using the <code>int</code> "|" operator) two or more
- * of those <code>SWT</code> style constants. The class description
- * lists the style constants that are applicable to the class.
- * Style bits are also inherited from superclasses.
- * </p>
- *
- * @param parent a widget which will be the parent of the new instance (cannot be null)
- * @param style the style of widget to construct
- *
- * @exception IllegalArgumentException <ul>
- * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
- * </ul>
- * @exception SWTException <ul>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
- * </ul>
- *
- * @see SWT#BORDER
- * @see SWT#FLAT
- * @see #getStyle
- */
-public ViewForm(Composite parent, int style) {
- super(parent, checkStyle(style));
-
- borderColor1 = new Color(getDisplay(), borderInsideRGB);
- borderColor2 = new Color(getDisplay(), borderMiddleRGB);
- borderColor3 = new Color(getDisplay(), borderOutsideRGB);
- setBorderVisible((style & SWT.BORDER) != 0);
-
- addPaintListener(new PaintListener() {
- public void paintControl(PaintEvent event) {
- onPaint(event.gc);
- }
- });
- addControlListener(new ControlAdapter(){
- public void controlResized(ControlEvent e) {
- onResize();
- }
- });
-
- addListener(SWT.Dispose, new Listener() {
- public void handleEvent(Event e) {
- onDispose();
- }
- });
-}
-/**
- * Check the style bits to ensure that no invalid styles are applied.
- * @private
- */
-private static int checkStyle (int style) {
- int mask = SWT.FLAT;
- return style & mask | SWT.NO_REDRAW_RESIZE;
-}
-public Point computeSize(int wHint, int hHint, boolean changed) {
- checkWidget();
- // size of title bar area
- Point leftSize = new Point(0, 0);
- if (topLeft != null) {
- leftSize = topLeft.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- leftSize.x += 1; // +1 for highlight line
- }
- Point centerSize = new Point(0, 0);
- if (topCenter != null) {
- centerSize = topCenter.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- }
- Point rightSize = new Point(0, 0);
- if (topRight != null) {
- rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- }
- Point size = new Point(0, 0);
- // calculate width of title bar
- if (separateTopCenter ||
- (wHint != SWT.DEFAULT && leftSize.x + centerSize.x + rightSize.x > wHint)) {
- size.x = leftSize.x + rightSize.x;
- size.x = Math.max(centerSize.x, size.x);
- size.y = Math.max(leftSize.y, rightSize.y) + 1; // +1 for highlight line
- if (topCenter != null){
- size.y += centerSize.y;
- }
- } else {
- size.x = leftSize.x + centerSize.x + rightSize.x;
- size.y = Math.max(leftSize.y, Math.max(centerSize.y, rightSize.y)) + 1; // +1 for highlight line
- }
-
- if (content != null) {
- Point contentSize = new Point(0, 0);
- contentSize = content.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- size.x = Math.max (size.x, contentSize.x);
- size.y += contentSize.y + 1; // +1 for line bewteen content and header
- }
-
- size.x += 2 * marginWidth;
- size.y += 2 * marginHeight;
-
- if (wHint != SWT.DEFAULT) size.x = wHint;
- if (hHint != SWT.DEFAULT) size.y = hHint;
-
- Rectangle trim = computeTrim(0, 0, size.x, size.y);
- return new Point (trim.width, trim.height);
-}
-public Rectangle computeTrim (int x, int y, int width, int height) {
- checkWidget ();
- int trimX = x - BORDER_LEFT;
- int trimY = y - BORDER_TOP;
- int trimWidth = width + BORDER_LEFT + BORDER_RIGHT;
- int trimHeight = height + BORDER_TOP + BORDER_BOTTOM;
- return new Rectangle(trimX, trimY, trimWidth, trimHeight);
-}
-public Rectangle getClientArea() {
- checkWidget();
- Rectangle clientArea = super.getClientArea();
- clientArea.x += BORDER_LEFT;
- clientArea.y += BORDER_TOP;
- clientArea.width -= BORDER_LEFT + BORDER_RIGHT;
- clientArea.height -= BORDER_TOP + BORDER_BOTTOM;
- return clientArea;
-}
-/**
-* Returns the content area.
-*
-* @return the control in the content area of the pane or null
-*/
-public Control getContent() {
- //checkWidget();
- return content;
-}
-/**
-* Returns Control that appears in the top center of the pane.
-* Typically this is a toolbar.
-*
-* @return the control in the top center of the pane or null
-*/
-public Control getTopCenter() {
- //checkWidget();
- return topCenter;
-}
-/**
-* Returns the Control that appears in the top left corner of the pane.
-* Typically this is a label such as CLabel.
-*
-* @return the control in the top left corner of the pane or null
-*/
-public Control getTopLeft() {
- //checkWidget();
- return topLeft;
-}
-/**
-* Returns the control in the top right corner of the pane.
-* Typically this is a Close button or a composite with a Menu and Close button.
-*
-* @return the control in the top right corner of the pane or null
-*/
-public Control getTopRight() {
- //checkWidget();
- return topRight;
-}
-public void layout (boolean changed) {
- checkWidget();
- Rectangle rect = getClientArea();
-
- drawLine1 = -1;
- drawLine2 = -1;
-
- Point leftSize = new Point(0, 0);
- if (topLeft != null && !topLeft.isDisposed()) {
- leftSize = topLeft.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- }
- Point centerSize = new Point(0, 0);
- if (topCenter != null && !topCenter.isDisposed()) {
- centerSize = topCenter.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- }
- Point rightSize = new Point(0, 0);
- if (topRight != null && !topRight.isDisposed()) {
- rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- }
-
- int minTopWidth = leftSize.x + centerSize.x + rightSize.x + 2*marginWidth + 1; // +1 for highlight line
- int height = rect.y + marginHeight;
-
- boolean top = false;
- if (separateTopCenter || minTopWidth > rect.width) {;
- int topHeight = Math.max(rightSize.y, leftSize.y);
- if (topRight != null && !topRight.isDisposed()) {
- top = true;
- topRight.setBounds(rect.x + rect.width - marginWidth - rightSize.x,
- rect.y + 1 + marginHeight,
- rightSize.x, topHeight);
- height += 1 + topHeight; // +1 for highlight line
- }
- if (topLeft != null && !topLeft.isDisposed()) {
- top = true;
- leftSize = topLeft.computeSize(rect.width - 2* marginWidth - rightSize.x - 1, SWT.DEFAULT);
- topLeft.setBounds(rect.x + 1 + marginWidth,
- rect.y + 1 + marginHeight,
- leftSize.x, topHeight);
- height = Math.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
- }
- if (topCenter != null && !topCenter.isDisposed()) {
- top = true;
- if (height > rect.y + marginHeight) {
- drawLine1 = height;
- height += 1; // +1 for divider line
- }
- centerSize = topCenter.computeSize(rect.width - 2 * marginWidth, SWT.DEFAULT);
- topCenter.setBounds(rect.x + rect.width - marginWidth - centerSize.x,
- height,
- centerSize.x, centerSize.y);
- height += centerSize.y;
-
- }
- } else {
- int topHeight = Math.max(rightSize.y, Math.max(centerSize.y, leftSize.y));
- if (topRight != null && !topRight.isDisposed()) {
- top = true;
- topRight.setBounds(rect.x + rect.width - marginWidth - rightSize.x,
- rect.y + marginHeight + 1, // +1 for highlight line
- rightSize.x, topHeight);
- height += 1 + topHeight; // +1 for highlight line
- }
- if (topCenter != null && !topCenter.isDisposed()) {
- top = true;
- topCenter.setBounds(rect.x + rect.width - marginWidth - rightSize.x - centerSize.x,
- rect.y + marginHeight + 1, // +1 for highlight line
- centerSize.x, topHeight);
- height = Math.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
- }
- if (topLeft != null && !topLeft.isDisposed()) {
- top = true;
- leftSize = topLeft.computeSize(rect.width - 2 * marginWidth - rightSize.x - centerSize.x - 1, topHeight);
- topLeft.setBounds(rect.x + marginWidth + 1, // +1 for highlight line
- rect.y + marginHeight + 1, // +1 for highlight line
- leftSize.x, topHeight);
- height = Math.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
- }
- }
-
- if (content != null && !content.isDisposed()) {
- if (top) {
- drawLine2 = height;
- height += 1; // +1 for divider line
- }
- content.setBounds(rect.x + marginWidth,
- height,
- rect.width - 2 * marginWidth,
- rect.y + rect.height - height - marginHeight);
- }
-}
-private void onDispose() {
- if (borderColor1 != null) {
- borderColor1.dispose();
- }
- borderColor1 = null;
-
- if (borderColor2 != null) {
- borderColor2.dispose();
- }
- borderColor2 = null;
-
- if (borderColor3 != null) {
- borderColor3.dispose();
- }
- borderColor3 = null;
-
- topLeft = null;
- topCenter = null;
- topRight = null;
- content = null;
- oldArea = null;
-}
-/**
-* Draws the focus border.
-*/
-private void onPaint(GC gc) {
- Rectangle d = super.getClientArea();
-
- if (showBorder) {
- if ((getStyle() & SWT.FLAT) !=0) {
- gc.setForeground(borderColor1);
- gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height - 1);
- } else {
- gc.setForeground(borderColor1);
- gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height - 3);
-
- gc.setForeground(borderColor2);
- gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y + d.height - 2);
- gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y + d.height - 1);
-
- gc.setForeground(borderColor3);
- gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y + d.height - 1);
- gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y + d.height - 2);
- }
- }
-
- if (drawLine1 != -1) {
- // top seperator line
- gc.setForeground(borderColor1);
- gc.drawLine(d.x + BORDER_LEFT, drawLine1, d.x + d.width - BORDER_RIGHT, drawLine1);
- }
- if (drawLine2 != -1) {
- // content separator line
- gc.setForeground(borderColor1);
- gc.drawLine(d.x + BORDER_LEFT, drawLine2, d.x + d.width - BORDER_RIGHT, drawLine2);
- }
- // highlight on top
- int y = drawLine1;
- if (y == -1){
- y = drawLine2;
- }
- if (y != -1) {
- gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
- gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP + marginHeight,
- d.x + BORDER_LEFT + marginWidth, y - 1);
- gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP + marginHeight,
- d.x + d.width - BORDER_RIGHT - marginWidth - 1, d.y + BORDER_TOP + marginHeight);
- }
-
- gc.setForeground(getForeground());
-}
-private void onResize() {
- layout();
-
- Rectangle area = super.getClientArea();
- if (oldArea == null || oldArea.width == 0 || oldArea.height == 0) {
- redraw();
- } else {
- int width = 0;
- if (oldArea.width < area.width) {
- width = area.width - oldArea.width + BORDER_RIGHT;
- } else if (oldArea.width > area.width) {
- width = BORDER_RIGHT;
- }
- redraw(area.x + area.width - width, area.y, width, area.height, false);
-
- int height = 0;
- if (oldArea.height < area.height) {
- height = area.height - oldArea.height + BORDER_BOTTOM;
- }
- if (oldArea.height > area.height) {
- height = BORDER_BOTTOM;
- }
- redraw(area.x, area.y + area.height - height, area.width, height, false);
- }
- oldArea = area;
-}
-/**
-* Sets the content.
-* Setting the content to null will remove it from
-* the pane - however, the creator of the content must dispose of the content.
-*
-* @param c the control to be displayed in the content area or null
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* </ul>
-*/
-public void setContent(Control content) {
- checkWidget();
- if (content != null && content.getParent() != this) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (this.content != null && !this.content.isDisposed()) {
- this.content.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
- }
- this.content = content;
- layout();
-}
-
-/**
-* Set the widget font.
-* This will apply the font to the topLeft, topRight and topCenter widgets.
-*/
-public void setFont(Font f) {
- super.setFont(f);
- if (topLeft != null && !topLeft.isDisposed()) topLeft.setFont(f);
- if (topCenter != null && !topCenter.isDisposed()) topCenter.setFont(f);
- if (topRight != null && !topRight.isDisposed()) topRight.setFont(f);
-
- layout();
-}
-/**
- * Sets the layout which is associated with the receiver to be
- * the argument which may be null.
- * <p>
- * Note : ViewForm does not use a layout class to size and position its children.
- * </p>
- *
- * @param the receiver's new layout or null
- *
- * @exception SWTException <ul>
- * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
- * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
- * </ul>
- */
-public void setLayout (Layout layout) {
- checkWidget();
- return;
-}
-/**
-* Set the control that appears in the top center of the pane.
-* Typically this is a toolbar.
-* The topCenter is optional. Setting the topCenter to null will remove it from
-* the pane - however, the creator of the topCenter must dispose of the topCenter.
-*
-* @param c the control to be displayed in the top center or null
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* </ul>
-*/
-public void setTopCenter(Control topCenter) {
- checkWidget();
- if (topCenter != null && topCenter.getParent() != this) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (this.topCenter != null && !this.topCenter.isDisposed()) {
- this.topCenter.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
- }
- this.topCenter = topCenter;
- layout();
-}
-/**
-* Set the control that appears in the top left corner of the pane.
-* Typically this is a label such as CLabel.
-* The topLeft is optional. Setting the top left control to null will remove it from
-* the pane - however, the creator of the control must dispose of the control.
-*
-* @param c the control to be displayed in the top left corner or null
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* </ul>
-*/
-public void setTopLeft(Control c) {
- checkWidget();
- if (c != null && c.getParent() != this) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (this.topLeft != null && !this.topLeft.isDisposed()) {
- this.topLeft.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
- }
- this.topLeft = c;
- layout();
-}
-/**
-* Set the control that appears in the top right corner of the pane.
-* Typically this is a Close button or a composite with a Menu and Close button.
-* The topRight is optional. Setting the top right control to null will remove it from
-* the pane - however, the creator of the control must dispose of the control.
-*
-* @param c the control to be displayed in the top right corner or null
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this ViewForm</li>
-* </ul>
-*/
-public void setTopRight(Control c) {
- checkWidget();
- if (c != null && c.getParent() != this) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- if (this.topRight != null && !this.topRight.isDisposed()) {
- this.topRight.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
- }
- this.topRight = c;
- layout();
-}
-/**
-* Specify whether the border should be displayed or not.
-*
-* @param show true if the border should be displayed
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* </ul>
-*/
-public void setBorderVisible(boolean show) {
- checkWidget();
- if (showBorder == show) return;
-
- showBorder = show;
- if (showBorder) {
- if ((getStyle() & SWT.FLAT)!= 0) {
- BORDER_LEFT = BORDER_TOP = BORDER_RIGHT = BORDER_BOTTOM = 1;
- } else {
- BORDER_LEFT = BORDER_TOP = 1;
- BORDER_RIGHT = BORDER_BOTTOM = 3;
- }
- } else {
- BORDER_BOTTOM = BORDER_TOP = BORDER_LEFT = BORDER_RIGHT = 0;
- }
-
- layout();
- redraw();
-}
-/**
-* If true, the topCenter will always appear on a separate line by itself, otherwise the
-* topCenter will appear in the top row if there is room and will be moved to the second row if
-* required.
-*
-* @param show true if the topCenter will always appear on a separate line by itself
-*
-* @exception SWTException <ul>
-* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
-* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
-* </ul>
-*/
-public void setTopCenterSeparate(boolean show) {
- checkWidget();
- separateTopCenter = show;
- layout();
-}
-
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * Instances of this class implement a Composite that lays out three
+ * children horizontally and allows programmatic control of layout and
+ * border parameters. ViewForm is used in the workbench to implement a
+ * view's label/menu/toolbar local bar.
+ * <p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>BORDER, FLAT</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(None)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class ViewForm extends Composite {
+
+ /**
+ * marginWidth specifies the number of pixels of horizontal margin
+ * that will be placed along the left and right edges of the form.
+ *
+ * The default value is 0.
+ */
+ public int marginWidth = 0;
+ /**
+ * marginHeight specifies the number of pixels of vertical margin
+ * that will be placed along the top and bottom edges of the form.
+ *
+ * The default value is 0.
+ */
+ public int marginHeight = 0;
+
+ /**
+ * Color of innermost line of drop shadow border.
+ */
+ public static RGB borderInsideRGB = new RGB (132, 130, 132);
+ /**
+ * Color of middle line of drop shadow border.
+ */
+ public static RGB borderMiddleRGB = new RGB (143, 141, 138);
+ /**
+ * Color of outermost line of drop shadow border.
+ */
+ public static RGB borderOutsideRGB = new RGB (171, 168, 165);
+
+ // SWT widgets
+ private Control topLeft;
+ private Control topCenter;
+ private Control topRight;
+ private Control content;
+
+ // Configuration and state info
+ private boolean separateTopCenter = false;
+ private int drawLine1 = -1;
+ private int drawLine2 = -1;
+
+ private boolean showBorder = false;
+
+ private int BORDER_TOP = 0;
+ private int BORDER_BOTTOM = 0;
+ private int BORDER_LEFT = 0;
+ private int BORDER_RIGHT = 0;
+
+ private Color borderColor1;
+ private Color borderColor2;
+ private Color borderColor3;
+
+ private Rectangle oldArea;
+ private static final int OFFSCREEN = -200;
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see SWT#BORDER
+ * @see SWT#FLAT
+ * @see #getStyle
+ */
+public ViewForm(Composite parent, int style) {
+ super(parent, checkStyle(style));
+
+ borderColor1 = new Color(getDisplay(), borderInsideRGB);
+ borderColor2 = new Color(getDisplay(), borderMiddleRGB);
+ borderColor3 = new Color(getDisplay(), borderOutsideRGB);
+ setBorderVisible((style & SWT.BORDER) != 0);
+
+ addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent event) {
+ onPaint(event.gc);
+ }
+ });
+ addControlListener(new ControlAdapter(){
+ public void controlResized(ControlEvent e) {
+ onResize();
+ }
+ });
+
+ addListener(SWT.Dispose, new Listener() {
+ public void handleEvent(Event e) {
+ onDispose();
+ }
+ });
+}
+/**
+ * Check the style bits to ensure that no invalid styles are applied.
+ * @private
+ */
+private static int checkStyle (int style) {
+ int mask = SWT.FLAT;
+ return style & mask | SWT.NO_REDRAW_RESIZE;
+}
+public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ // size of title bar area
+ Point leftSize = new Point(0, 0);
+ if (topLeft != null) {
+ leftSize = topLeft.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ leftSize.x += 1; // +1 for highlight line
+ }
+ Point centerSize = new Point(0, 0);
+ if (topCenter != null) {
+ centerSize = topCenter.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ }
+ Point rightSize = new Point(0, 0);
+ if (topRight != null) {
+ rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ }
+ Point size = new Point(0, 0);
+ // calculate width of title bar
+ if (separateTopCenter ||
+ (wHint != SWT.DEFAULT && leftSize.x + centerSize.x + rightSize.x > wHint)) {
+ size.x = leftSize.x + rightSize.x;
+ size.x = Math.max(centerSize.x, size.x);
+ size.y = Math.max(leftSize.y, rightSize.y) + 1; // +1 for highlight line
+ if (topCenter != null){
+ size.y += centerSize.y;
+ }
+ } else {
+ size.x = leftSize.x + centerSize.x + rightSize.x;
+ size.y = Math.max(leftSize.y, Math.max(centerSize.y, rightSize.y)) + 1; // +1 for highlight line
+ }
+
+ if (content != null) {
+ Point contentSize = new Point(0, 0);
+ contentSize = content.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ size.x = Math.max (size.x, contentSize.x);
+ size.y += contentSize.y + 1; // +1 for line bewteen content and header
+ }
+
+ size.x += 2 * marginWidth;
+ size.y += 2 * marginHeight;
+
+ if (wHint != SWT.DEFAULT) size.x = wHint;
+ if (hHint != SWT.DEFAULT) size.y = hHint;
+
+ Rectangle trim = computeTrim(0, 0, size.x, size.y);
+ return new Point (trim.width, trim.height);
+}
+public Rectangle computeTrim (int x, int y, int width, int height) {
+ checkWidget ();
+ int trimX = x - BORDER_LEFT;
+ int trimY = y - BORDER_TOP;
+ int trimWidth = width + BORDER_LEFT + BORDER_RIGHT;
+ int trimHeight = height + BORDER_TOP + BORDER_BOTTOM;
+ return new Rectangle(trimX, trimY, trimWidth, trimHeight);
+}
+public Rectangle getClientArea() {
+ checkWidget();
+ Rectangle clientArea = super.getClientArea();
+ clientArea.x += BORDER_LEFT;
+ clientArea.y += BORDER_TOP;
+ clientArea.width -= BORDER_LEFT + BORDER_RIGHT;
+ clientArea.height -= BORDER_TOP + BORDER_BOTTOM;
+ return clientArea;
+}
+/**
+* Returns the content area.
+*
+* @return the control in the content area of the pane or null
+*/
+public Control getContent() {
+ //checkWidget();
+ return content;
+}
+/**
+* Returns Control that appears in the top center of the pane.
+* Typically this is a toolbar.
+*
+* @return the control in the top center of the pane or null
+*/
+public Control getTopCenter() {
+ //checkWidget();
+ return topCenter;
+}
+/**
+* Returns the Control that appears in the top left corner of the pane.
+* Typically this is a label such as CLabel.
+*
+* @return the control in the top left corner of the pane or null
+*/
+public Control getTopLeft() {
+ //checkWidget();
+ return topLeft;
+}
+/**
+* Returns the control in the top right corner of the pane.
+* Typically this is a Close button or a composite with a Menu and Close button.
+*
+* @return the control in the top right corner of the pane or null
+*/
+public Control getTopRight() {
+ //checkWidget();
+ return topRight;
+}
+public void layout (boolean changed) {
+ checkWidget();
+ Rectangle rect = getClientArea();
+
+ drawLine1 = -1;
+ drawLine2 = -1;
+
+ Point leftSize = new Point(0, 0);
+ if (topLeft != null && !topLeft.isDisposed()) {
+ leftSize = topLeft.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ }
+ Point centerSize = new Point(0, 0);
+ if (topCenter != null && !topCenter.isDisposed()) {
+ centerSize = topCenter.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ }
+ Point rightSize = new Point(0, 0);
+ if (topRight != null && !topRight.isDisposed()) {
+ rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ }
+
+ int minTopWidth = leftSize.x + centerSize.x + rightSize.x + 2*marginWidth + 1; // +1 for highlight line
+ int height = rect.y + marginHeight;
+
+ boolean top = false;
+ if (separateTopCenter || minTopWidth > rect.width) {;
+ int topHeight = Math.max(rightSize.y, leftSize.y);
+ if (topRight != null && !topRight.isDisposed()) {
+ top = true;
+ topRight.setBounds(rect.x + rect.width - marginWidth - rightSize.x,
+ rect.y + 1 + marginHeight,
+ rightSize.x, topHeight);
+ height += 1 + topHeight; // +1 for highlight line
+ }
+ if (topLeft != null && !topLeft.isDisposed()) {
+ top = true;
+ leftSize = topLeft.computeSize(rect.width - 2* marginWidth - rightSize.x - 1, SWT.DEFAULT);
+ topLeft.setBounds(rect.x + 1 + marginWidth,
+ rect.y + 1 + marginHeight,
+ leftSize.x, topHeight);
+ height = Math.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
+ }
+ if (topCenter != null && !topCenter.isDisposed()) {
+ top = true;
+ if (height > rect.y + marginHeight) {
+ drawLine1 = height;
+ height += 1; // +1 for divider line
+ }
+ centerSize = topCenter.computeSize(rect.width - 2 * marginWidth, SWT.DEFAULT);
+ topCenter.setBounds(rect.x + rect.width - marginWidth - centerSize.x,
+ height,
+ centerSize.x, centerSize.y);
+ height += centerSize.y;
+
+ }
+ } else {
+ int topHeight = Math.max(rightSize.y, Math.max(centerSize.y, leftSize.y));
+ if (topRight != null && !topRight.isDisposed()) {
+ top = true;
+ topRight.setBounds(rect.x + rect.width - marginWidth - rightSize.x,
+ rect.y + marginHeight + 1, // +1 for highlight line
+ rightSize.x, topHeight);
+ height += 1 + topHeight; // +1 for highlight line
+ }
+ if (topCenter != null && !topCenter.isDisposed()) {
+ top = true;
+ topCenter.setBounds(rect.x + rect.width - marginWidth - rightSize.x - centerSize.x,
+ rect.y + marginHeight + 1, // +1 for highlight line
+ centerSize.x, topHeight);
+ height = Math.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
+ }
+ if (topLeft != null && !topLeft.isDisposed()) {
+ top = true;
+ leftSize = topLeft.computeSize(rect.width - 2 * marginWidth - rightSize.x - centerSize.x - 1, topHeight);
+ topLeft.setBounds(rect.x + marginWidth + 1, // +1 for highlight line
+ rect.y + marginHeight + 1, // +1 for highlight line
+ leftSize.x, topHeight);
+ height = Math.max(height, rect.y + marginHeight + 1 + topHeight); // +1 for highlight line
+ }
+ }
+
+ if (content != null && !content.isDisposed()) {
+ if (top) {
+ drawLine2 = height;
+ height += 1; // +1 for divider line
+ }
+ content.setBounds(rect.x + marginWidth,
+ height,
+ rect.width - 2 * marginWidth,
+ rect.y + rect.height - height - marginHeight);
+ }
+}
+private void onDispose() {
+ if (borderColor1 != null) {
+ borderColor1.dispose();
+ }
+ borderColor1 = null;
+
+ if (borderColor2 != null) {
+ borderColor2.dispose();
+ }
+ borderColor2 = null;
+
+ if (borderColor3 != null) {
+ borderColor3.dispose();
+ }
+ borderColor3 = null;
+
+ topLeft = null;
+ topCenter = null;
+ topRight = null;
+ content = null;
+ oldArea = null;
+}
+/**
+* Draws the focus border.
+*/
+private void onPaint(GC gc) {
+ Rectangle d = super.getClientArea();
+
+ if (showBorder) {
+ if ((getStyle() & SWT.FLAT) !=0) {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height - 1);
+ } else {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height - 3);
+
+ gc.setForeground(borderColor2);
+ gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y + d.height - 2);
+ gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y + d.height - 1);
+
+ gc.setForeground(borderColor3);
+ gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y + d.height - 1);
+ gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y + d.height - 2);
+ }
+ }
+
+ if (drawLine1 != -1) {
+ // top seperator line
+ gc.setForeground(borderColor1);
+ gc.drawLine(d.x + BORDER_LEFT, drawLine1, d.x + d.width - BORDER_RIGHT, drawLine1);
+ }
+ if (drawLine2 != -1) {
+ // content separator line
+ gc.setForeground(borderColor1);
+ gc.drawLine(d.x + BORDER_LEFT, drawLine2, d.x + d.width - BORDER_RIGHT, drawLine2);
+ }
+ // highlight on top
+ int y = drawLine1;
+ if (y == -1){
+ y = drawLine2;
+ }
+ if (y != -1) {
+ gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+ gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP + marginHeight,
+ d.x + BORDER_LEFT + marginWidth, y - 1);
+ gc.drawLine(d.x + BORDER_LEFT + marginWidth, d.y + BORDER_TOP + marginHeight,
+ d.x + d.width - BORDER_RIGHT - marginWidth - 1, d.y + BORDER_TOP + marginHeight);
+ }
+
+ gc.setForeground(getForeground());
+}
+private void onResize() {
+ layout();
+
+ Rectangle area = super.getClientArea();
+ if (oldArea == null || oldArea.width == 0 || oldArea.height == 0) {
+ redraw();
+ } else {
+ int width = 0;
+ if (oldArea.width < area.width) {
+ width = area.width - oldArea.width + BORDER_RIGHT;
+ } else if (oldArea.width > area.width) {
+ width = BORDER_RIGHT;
+ }
+ redraw(area.x + area.width - width, area.y, width, area.height, false);
+
+ int height = 0;
+ if (oldArea.height < area.height) {
+ height = area.height - oldArea.height + BORDER_BOTTOM;
+ }
+ if (oldArea.height > area.height) {
+ height = BORDER_BOTTOM;
+ }
+ redraw(area.x, area.y + area.height - height, area.width, height, false);
+ }
+ oldArea = area;
+}
+/**
+* Sets the content.
+* Setting the content to null will remove it from
+* the pane - however, the creator of the content must dispose of the content.
+*
+* @param c the control to be displayed in the content area or null
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* </ul>
+*/
+public void setContent(Control content) {
+ checkWidget();
+ if (content != null && content.getParent() != this) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (this.content != null && !this.content.isDisposed()) {
+ this.content.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
+ }
+ this.content = content;
+ layout();
+}
+
+/**
+* Set the widget font.
+* This will apply the font to the topLeft, topRight and topCenter widgets.
+*/
+public void setFont(Font f) {
+ super.setFont(f);
+ if (topLeft != null && !topLeft.isDisposed()) topLeft.setFont(f);
+ if (topCenter != null && !topCenter.isDisposed()) topCenter.setFont(f);
+ if (topRight != null && !topRight.isDisposed()) topRight.setFont(f);
+
+ layout();
+}
+/**
+ * Sets the layout which is associated with the receiver to be
+ * the argument which may be null.
+ * <p>
+ * Note : ViewForm does not use a layout class to size and position its children.
+ * </p>
+ *
+ * @param the receiver's new layout or null
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLayout (Layout layout) {
+ checkWidget();
+ return;
+}
+/**
+* Set the control that appears in the top center of the pane.
+* Typically this is a toolbar.
+* The topCenter is optional. Setting the topCenter to null will remove it from
+* the pane - however, the creator of the topCenter must dispose of the topCenter.
+*
+* @param c the control to be displayed in the top center or null
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* </ul>
+*/
+public void setTopCenter(Control topCenter) {
+ checkWidget();
+ if (topCenter != null && topCenter.getParent() != this) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (this.topCenter != null && !this.topCenter.isDisposed()) {
+ this.topCenter.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
+ }
+ this.topCenter = topCenter;
+ layout();
+}
+/**
+* Set the control that appears in the top left corner of the pane.
+* Typically this is a label such as CLabel.
+* The topLeft is optional. Setting the top left control to null will remove it from
+* the pane - however, the creator of the control must dispose of the control.
+*
+* @param c the control to be displayed in the top left corner or null
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* </ul>
+*/
+public void setTopLeft(Control c) {
+ checkWidget();
+ if (c != null && c.getParent() != this) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (this.topLeft != null && !this.topLeft.isDisposed()) {
+ this.topLeft.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
+ }
+ this.topLeft = c;
+ layout();
+}
+/**
+* Set the control that appears in the top right corner of the pane.
+* Typically this is a Close button or a composite with a Menu and Close button.
+* The topRight is optional. Setting the top right control to null will remove it from
+* the pane - however, the creator of the control must dispose of the control.
+*
+* @param c the control to be displayed in the top right corner or null
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this ViewForm</li>
+* </ul>
+*/
+public void setTopRight(Control c) {
+ checkWidget();
+ if (c != null && c.getParent() != this) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (this.topRight != null && !this.topRight.isDisposed()) {
+ this.topRight.setBounds(OFFSCREEN, OFFSCREEN, 0, 0);
+ }
+ this.topRight = c;
+ layout();
+}
+/**
+* Specify whether the border should be displayed or not.
+*
+* @param show true if the border should be displayed
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* </ul>
+*/
+public void setBorderVisible(boolean show) {
+ checkWidget();
+ if (showBorder == show) return;
+
+ showBorder = show;
+ if (showBorder) {
+ if ((getStyle() & SWT.FLAT)!= 0) {
+ BORDER_LEFT = BORDER_TOP = BORDER_RIGHT = BORDER_BOTTOM = 1;
+ } else {
+ BORDER_LEFT = BORDER_TOP = 1;
+ BORDER_RIGHT = BORDER_BOTTOM = 3;
+ }
+ } else {
+ BORDER_BOTTOM = BORDER_TOP = BORDER_LEFT = BORDER_RIGHT = 0;
+ }
+
+ layout();
+ redraw();
+}
+/**
+* If true, the topCenter will always appear on a separate line by itself, otherwise the
+* topCenter will appear in the top row if there is room and will be moved to the second row if
+* required.
+*
+* @param show true if the topCenter will always appear on a separate line by itself
+*
+* @exception SWTException <ul>
+* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+* </ul>
+*/
+public void setTopCenterSeparate(boolean show) {
+ checkWidget();
+ separateTopCenter = show;
+ layout();
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/WrappedContent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/WrappedContent.java
index b7fd249aed..7d73a3b941 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/WrappedContent.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/WrappedContent.java
@@ -1,667 +1,667 @@
-package org.eclipse.swt.custom;
-
-/*
+package org.eclipse.swt.custom;
+
+/*
* Copyright (c) 2001, 2002 IBM Corp. All rights reserved.
* This file is made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- */
-
-import org.eclipse.swt.*;
-import org.eclipse.swt.graphics.*;
-import org.eclipse.swt.internal.*;
-
-/**
- * An instance of class <code>WrappedContent</code> is used by
- * StyledText to display wrapped lines. Lines are wrapped at word
- * breaks which are marked by a space character. Trailing space
- * behind words is kept on the current line.
- * If the last remaining word on a line can not be fully displayed
- * the line is wrapped character by character.
- * WrappedContent wraps a StyledTextContent which provides the line
- * data. The start offset and length of wrapped lines is calculated
- * and updated based on recalculation requests and text changes.
- * <p>
- * All public methods in this class implement the
- * <code>StyledTextContent</code> interface. Package visible
- * methods are internal API for use by <code>StyledText</code>.
- * </p>
- */
-class WrappedContent implements StyledTextContent {
- final static int LINE_OFFSET = 0; // index of line offset in visualLines array
- final static int LINE_LENGTH = 1; // index of line lenght in visualLines array
- final static int WRAP_LINE_LENGTH = 0;
- final static int WRAP_LINE_WIDTH = 1;
-
- StyledTextRenderer renderer;
- StyledTextContent logicalContent;
- int[][] visualLines; // start and length of each visual line
- int visualLineCount = 0;
-
-/**
- * Create a new instance.
- *
- * @param renderer <class>StyledTextRenderer</class> that renders
- * the lines wrapped by the new instance.
- * @param logicalContent StyledTextContent that provides the line
- * data.
- */
-WrappedContent(StyledTextRenderer renderer, StyledTextContent logicalContent) {
- this.renderer = renderer;
- this.logicalContent = logicalContent;
-}
-/**
- * @see StyledTextContent#addTextChangeListener(TextChangeListener)
- */
-public void addTextChangeListener(TextChangeListener listener) {
- logicalContent.addTextChangeListener(listener);
-}
-/**
- * Grow the lines array to at least the specified size.
- * <p>
- *
- * @param numLines number of elements that the array should have
- * at a minimum
- */
-private void ensureSize(int numLines) {
- int size = visualLines.length;
- if (size >= numLines) {
- return;
- }
- int[][] newLines = new int[Math.max(size * 2, numLines)][2];
- System.arraycopy(visualLines, 0, newLines, 0, size);
- visualLines = newLines;
- resetVisualLines(size, visualLines.length - size);
-}
-/**
- * @see StyledTextContent#getCharCount()
- */
-public int getCharCount() {
- return logicalContent.getCharCount();
-}
-/**
- * @return the visual (wrapped) line at the specified index
- * @see StyledTextContent#getLine(int)
- */
-public String getLine(int lineIndex) {
- String line;
-
- // redirect call to logical content if there are no wrapped lines
- if (visualLineCount == 0) {
- line = logicalContent.getLine(lineIndex);
- }
- else {
- if (lineIndex >= visualLineCount || lineIndex < 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- line = logicalContent.getTextRange(visualLines[lineIndex][LINE_OFFSET], visualLines[lineIndex][LINE_LENGTH]);
- }
- return line;
-}
-/**
- * Returns the visual (wrapped) line at given offset.
- * <p>
- * The offset is ambiguous if it identifies the end of a visual line and
- * there is another visual line below. In this case the end of the visual
- * line has the same offset as the beginning of the next visual line
- * since the visual line break is not represented by any character in the
- * logical line.
- * In this ambiguous case the offset is assumed to represent the end of a
- * visual line and the index of the first visual line is returned.
- * </p>
- *
- * @param offset offset of the desired line.
- * @return the index of the visual (wrapped) line at the specified offset
- * @see StyledTextContent#getLineAtOffset(int)
- */
-public int getLineAtOffset(int offset) {
- int lastLine = visualLineCount - 1;
- int lastChar;
-
- // redirect call to logical content if there are no wrapped lines
- if (visualLineCount == 0) {
- return logicalContent.getLineAtOffset(offset);
- }
- // can't use getCharCount to get the number of characters since this
- // method is called in textChanged, when the logicalContent used by
- // getCharCount has already changed. at that point the visual lines
- // have not been updated yet and we thus need to use the old character
- // count which is only available in the visual content.
- lastChar = visualLines[lastLine][LINE_OFFSET] + visualLines[lastLine][LINE_LENGTH];
- if (offset < 0 || (offset > 0 && offset > lastChar)) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- // if last line and the line is not empty you can ask for
- // a position that doesn't exist (the one to the right of the
- // last character) - for inserting
- if (offset == lastChar) {
- return lastLine;
- }
-
- int high = visualLineCount;
- int low = -1;
- int index = visualLineCount;
- while (high - low > 1) {
- index = (high + low) / 2;
- int lineStart = visualLines[index][LINE_OFFSET];
- if (offset >= lineStart) {
- int lineEnd = lineStart + visualLines[index][LINE_LENGTH];
- low = index;
- if (offset <= lineEnd) {
- break;
- }
- }
- else {
- high = index;
- }
- }
- if (low > 0 && offset == visualLines[low - 1][LINE_OFFSET] + visualLines[low - 1][LINE_LENGTH]) {
- // end of a visual line/beginning of next visual line is ambiguous
- // (they have the same offset). always return the first visual line
- low--;
- }
- return low;
-}
-/**
- * @return the number of visual (wrapped) lines
- * @see StyledTextContent#getLineCount()
- */
-public int getLineCount() {
- int lineCount = visualLineCount;
-
- // redirect call to logical content if there are no wrapped lines
- if (visualLineCount == 0) {
- lineCount = logicalContent.getLineCount();
- }
- return lineCount;
-}
-/**
- * @see StyledTextContent#getLineDelimiter()
- */
-public String getLineDelimiter() {
- return logicalContent.getLineDelimiter();
-}
-/**
- * @return the start offset of the visual (wrapped) line at the given
- * index
- * @see StyledTextContent#getOffsetAtLine(int)
- */
-public int getOffsetAtLine(int lineIndex) {
- int offset;
-
- // redirect call to logical content if there are no wrapped lines
- if (visualLineCount == 0) {
- offset = logicalContent.getOffsetAtLine(lineIndex);
- }
- else {
- if (lineIndex >= visualLineCount || lineIndex < 0) {
- SWT.error(SWT.ERROR_INVALID_ARGUMENT);
- }
- offset = visualLines[lineIndex][LINE_OFFSET];
- }
- return offset;
-}
-/**
- * @see StyledTextContent#getTextRange(int, int)
- */
-public String getTextRange(int start, int length) {
- return logicalContent.getTextRange(start, length);
-}
-/**
- * Returns the number of visual (wrapped) lines.
- *
- * @return the number of visual (wrapped) lines
- */
-int getVisualLineCount() {
- return visualLineCount;
-}
-/**
- * Returns the offset of the character after the word at the specified
- * offset.
- * <p>
- * Words are separated by spaces. Trailing spaces are considered part
- * of the word.
- * </p>
- *
- * @param line logical line the word is in
- * @param startOffset start offset of the line, relative to the start
- * of the logical line.
- * @param offset offset of the word to return the end of, relative to
- * the start of the visual line.
- * @return the offset of the character after the word at the specified
- * offset.
- */
-private int getWordEnd(String line, int startOffset, int offset) {
- int lineLength = line.length();
-
- offset += startOffset;
- if (offset >= lineLength) {
- return offset - startOffset;
- }
- // skip over leading whitespace
- do {
- offset++;
- } while (offset < lineLength && Compatibility.isSpaceChar(line.charAt(offset)));
- while (offset < lineLength && Compatibility.isSpaceChar(line.charAt(offset)) == false) {
- offset++;
- }
- // skip over trailing whitespace
- while (offset < lineLength && Compatibility.isSpaceChar(line.charAt(offset))) {
- offset++;
- }
- return offset - startOffset;
-}
-/**
- * Returns the start offset of the word at the specified offset.
- * There are two classes of words formed by a sequence of characters:
- * <p>
- * Words are separated by spaces. Trailing spaces are considered part
- * of the word.
- * </p>
- *
- * @param line logical line the word is in
- * @param startOffset start offset of the line, relative to the start
- * of the logical line.
- * @param offset offset of the word to return the start of, relative to
- * the start of the visual line.
- * @return the start offset of the word at the specified offset.
- */
-private int getWordStart(String line, int startOffset, int offset) {
- offset += startOffset;
- // skip over trailing whitespace
- do {
- offset--;
- } while (offset > startOffset && Compatibility.isSpaceChar(line.charAt(offset)));
- while (offset > startOffset && Compatibility.isSpaceChar(line.charAt(offset - 1)) == false) {
- offset--;
- }
- return offset - startOffset;
-}
-/**
- * @see StyledTextContent#removeTextChangeListener(TextChangeListener)
- */
-public void removeTextChangeListener(TextChangeListener listener) {
- logicalContent.removeTextChangeListener(listener);
-}
-/**
- * Reset the visual (wrapped) lines in the specified range.
- * If the range specifies partial logical lines (e.g., startLine is
- * the second of two visual lines) it is extended to reset all visual
- * lines of a logical line.
- * Following the reset the logical lines in the reset visual range are
- * rewrapped.
- * <p>
- *
- * @param startLine index of the first visual line
- * @param lineCount number of visual lines
- */
-void reset(int startLine, int lineCount) {
- if (lineCount <= 0 || visualLineCount == 0) {
- return;
- }
- reset(startLine, lineCount, true);
-}
-/**
- * Reset the visual (wrapped) lines in the specified range.
- * If the range specifies partial logical lines (e.g., startLine is
- * the second of two visual lines) it is extended to reset all visual
- * lines of a logical line.
- * <p>
- *
- * @param startLine index of the first visual line
- * @param lineCount number of visual lines
- * @param wrap true=rewrap the logical lines in the reset visual range
- * false=don't rewrap lines. Visual lines will be left in an inconsistent
- * state since there will be a range of unwrapped and unknown lines.
- * @return the first visual line that was reset
- */
-private int reset(int startLine, int lineCount, boolean wrap) {
- if (lineCount <= 0) {
- return startLine;
- }
- // make sure that all visual lines of the first logical line are
- // being reset. visualFirstLine is the first visual line of the
- // first logical line that has at least one visual line reset.
- int visualFirstLineOffset = getOffsetAtLine(startLine);
- int logicalFirstLine = logicalContent.getLineAtOffset(visualFirstLineOffset);
- int logicalFirstLineOffset = logicalContent.getOffsetAtLine(logicalFirstLine);
- int visualFirstLine = getLineAtOffset(logicalFirstLineOffset);
-
- lineCount += startLine - visualFirstLine;
- startLine = visualFirstLine;
-
- // make sure that all visual lines of the last logical line are
- // being reset.
- int lastLine = startLine + lineCount - 1;
- int lastLineEnd = visualLines[lastLine][LINE_OFFSET] + visualLines[lastLine][LINE_LENGTH];
- int logicalEndLine = 0;
-
- while (lastLine < visualLineCount - 1 && lastLineEnd == visualLines[lastLine + 1][LINE_OFFSET]) {
- lastLine++;
- lastLineEnd = visualLines[lastLine][LINE_OFFSET] + visualLines[lastLine][LINE_LENGTH];
- }
- if (wrap) {
- if (lastLine == visualLineCount - 1) {
- logicalEndLine = logicalContent.getLineCount();
- }
- else {
- logicalEndLine = logicalContent.getLineAtOffset(visualLines[lastLine + 1][LINE_OFFSET]);
- }
- }
- lineCount = lastLine - startLine + 1;
- resetVisualLines(startLine, lineCount);
- visualLineCount -= lineCount;
- if (wrap) {
- // always recalculate line wrap immediately after a reset
- // because the content always needs to be in a usable state.
- // i.e., there must not be any reset but unwrapped lines
- wrapLineRange(logicalFirstLine, logicalEndLine, startLine);
- }
- return startLine;
-}
-/**
- * Reset the visual (wrapped) lines in the specified range.
- * <p>
- *
- * @param startLine index of the first visual line
- * @param lineCount number of visual lines
- */
-private void resetVisualLines(int startLine, int lineCount) {
- int endLine = startLine + lineCount;
-
- for (int i = startLine; i < endLine; i++) {
- visualLines[i] = new int[] {-1, -1};
- }
-}
-/**
- * @see StyledTextContent#replaceTextRange(int, int, String)
- */
-public void replaceTextRange(int start, int replaceLength, String text) {
- logicalContent.replaceTextRange(start, replaceLength, text);
-}
-/**
- * @see StyledTextContent#setText(String)
- */
-public void setText(String text) {
- logicalContent.setText(text);
-}
-/**
- * Set the line wrap data for the specified visual (wrapped) line.
- * <p>
- *
- * @param visualLineIndex index of the visual line
- * @param visualLineOffset start offset of the visual line, relative
- * to the start of the document
- * @param visualLineLength length of the visual line
- */
-private void setVisualLine(int visualLineIndex, int visualLineOffset, int visualLineLength) {
- ensureSize(visualLineCount + 1);
- // is the space for the visual line already taken? can happen if
- // there are more visual lines for a given logical line than before
- if (visualLines[visualLineIndex][LINE_OFFSET] != -1) {
- System.arraycopy(visualLines, visualLineIndex, visualLines, visualLineIndex + 1, visualLineCount - visualLineIndex);
- visualLines[visualLineIndex] = new int[2];
- }
- visualLines[visualLineIndex][LINE_OFFSET] = visualLineOffset;
- visualLines[visualLineIndex][LINE_LENGTH] = visualLineLength;
- visualLineCount++;
-}
-/**
- * Recalculates the line wrap for the lines affected by the
- * text change.
- * <p>
- *
- * @param startOffset the start offset of the text change
- * @param newLineCount the number of inserted lines
- * @param replaceLineCount the number of deleted lines
- * @param newCharCount the number of new characters
- * @param replaceCharCount the number of deleted characters
- */
-void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
- // do nothing if there are no wrapped lines
- if (visualLineCount == 0) {
- return;
- }
- int logicalStartLine = logicalContent.getLineAtOffset(startOffset);
- int visualStartLine = getLineAtOffset(startOffset);
- int visualReplaceLastLine = visualLineCount - 1;
- int textChangeDelta = newCharCount - replaceCharCount;
-
- if (replaceLineCount > 0) {
- visualReplaceLastLine = getLineAtOffset(startOffset + replaceCharCount);
- // at the start of a visual line/end of the previous visual line?
- if ((visualReplaceLastLine == 0 ||
- visualLines[visualReplaceLastLine][LINE_OFFSET] == visualLines[visualReplaceLastLine - 1][LINE_OFFSET] + visualLines[visualReplaceLastLine - 1][LINE_LENGTH]) &&
- visualReplaceLastLine != visualLineCount - 1) {
- visualReplaceLastLine++;
- }
- visualStartLine = reset(visualStartLine, visualReplaceLastLine - visualStartLine + 1, false);
- }
- else {
- visualStartLine = reset(visualStartLine, 1, false);
- }
- visualReplaceLastLine = wrapLineRange(logicalStartLine, logicalStartLine + 1 + newLineCount, visualStartLine);
- for (int i = visualReplaceLastLine; i < visualLineCount; i++) {
- visualLines[i][LINE_OFFSET] += textChangeDelta;
- }
-}
-/**
- * Measure the width of a segment in the specified logical line.
- * <p>
- *
- * @param line the logical line
- * @param logicalLineOffset start offset of the logical line, relative
- * to the start of the document
- * @param visualLineOffset offset to start measuring at/start offset
- * of the visual line
- * @param visualLineLength length of the segment to measure/the visual
- * line
- * @param styles StyleRanges to use during measuring
- * @param startX x position of the visual line relative to the start
- * of the logical line
- * @param gc GC to use for measuring
- */
-private int getTextWidth(String line, int logicalLineOffset, int visualLineOffset, int visualLineLength, StyleRange[] styles, int startX, GC gc) {
- int width;
-
- if (styles != null) {
- // while wrapping a line, the logcial line styles may contain
- // style ranges that don't apply (i.e., completely on the previous/next
- // visual line). Therefore we need to filter the logical lines.
- styles = renderer.getVisualLineStyleData(styles, logicalLineOffset + visualLineOffset, visualLineLength);
- }
- if (renderer.isBidi()) {
- String wrappedLine = line.substring(visualLineOffset, visualLineOffset + visualLineLength);
- StyledTextBidi bidi = renderer.getStyledTextBidi(wrappedLine, logicalLineOffset + visualLineOffset, gc, styles);
- width = bidi.getTextWidth();
- }
- else {
- width = renderer.getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
- }
- return width;
-}
-/**
- * Wrap the given logical line at the specified offset.
- * Called repeatedly until the entire logical lines has been split into
- * visual (wrapped) lines.
- * <p>
- *
- * @param line the logical line
- * @param logicalLineOffset offset of the logical line, relative to the
- * beginning of the content
- * @param visualLineOffset start offset of the new visual line, relative
- * to the start of the logical line.
- * @param startX x position of visualLineOffset, relative to the beginning
- * of the logical line
- * @param width width to wrap the line to
- * @param numChars average number of characters that fit into width
- * @param styles StyleRanges to use for measuring the wrapped line
- * @param gc GC to use for measuring
- * @return int[0]=length of the new visual line, int[1]=width in pixels of
- * the new visual line
- */
-private int[] wrapLine(String line, int logicalLineOffset, int visualLineOffset, int startX, int width, int numChars, StyleRange[] styles, GC gc) {
- int lineLength = line.length();
- int lineWidth = 0;
- int visualLineLength;
-
- numChars = Math.min(numChars, lineLength - visualLineOffset);
- visualLineLength = getWordStart(line, visualLineOffset, numChars);
- // find a word that is within the client area. make sure at least one
- // character is on each line so that line wrap algorithm terminates.
- if (visualLineLength > 0) {
- lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
- if (lineWidth >= width) {
- while (visualLineLength > 1 && lineWidth >= width) {
- visualLineLength = getWordStart(line, visualLineOffset, visualLineLength);
- lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
- }
- }
- else
- if (lineWidth < width) {
- while (visualLineOffset + visualLineLength < lineLength) {
- int newLineLength = getWordEnd(line, visualLineOffset, visualLineLength);
- int newLineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, newLineLength, styles, startX, gc);
- // would next word be beyond client area?
- if (newLineWidth >= width) {
- break;
- }
- else {
- visualLineLength = newLineLength;
- lineWidth = newLineWidth;
- }
- }
- }
- }
- if (visualLineLength <= 0) {
- // no complete word fits on the line. either first word was not within
- // estimated number of characters or it was beyond the line width even
- // though it was within numChars.
- visualLineLength = numChars;
- lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
- if (lineWidth >= width) {
- while (visualLineLength > 1 && lineWidth >= width) {
- visualLineLength--;
- lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
- }
- }
- else
- if (lineWidth < width) {
- while (visualLineOffset + visualLineLength < lineLength) {
- int newLineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength + 1, styles, startX, gc);
- if (newLineWidth >= width) {
- break;
- }
- else {
- visualLineLength++;
- lineWidth = newLineWidth;
- }
- }
- }
- }
- return new int[] {visualLineLength, lineWidth};
-}
-/**
- * Wrap the logical lines in the given range at the current client
- * area width of the StyledText widget
- * <p>
- *
- * @param startLine first logical line to wrap
- * @param endLine line after last logical line
- * @param visualLineIndex visual (wrapped) line index that startLine
- * corresponds to.
- * @return index of the line following the last wrapped line
- */
-private int wrapLineRange(int startLine, int endLine, int visualLineIndex) {
- int emptyLineCount = 0;
-
- visualLineIndex = wrapLineRange(startLine, endLine, visualLineIndex, renderer.getClientArea().width);
- // is there space left for more visual lines? can happen if there are fewer
- // visual lines for a given logical line than before
- for (int i = visualLineIndex; i < visualLines.length; i++, emptyLineCount++) {
- if (visualLines[i][LINE_OFFSET] != -1) {
- break;
- }
- }
- if (emptyLineCount > 0) {
- int copyLineCount = visualLineCount - visualLineIndex;
- System.arraycopy(visualLines, visualLineIndex + emptyLineCount, visualLines, visualLineIndex, copyLineCount);
- resetVisualLines(visualLineIndex + copyLineCount, emptyLineCount);
- }
- return visualLineIndex;
-}
-/**
- * Wrap the lines in the given range. Skip lines that have already
- * been wrapped.
- * <p>
- *
- * @param startLine first logical line to wrap
- * @param endLine line after last logical line
- * @param visualLineIndex visual (wrapped) line index that startLine
- * corresponds to.
- * @param width line width to wrap at
- * @return index of last wrapped line
- */
-private int wrapLineRange(int startLine, int endLine, int visualLineIndex, int width) {
- // if there are no wrapped lines and the width is 0 the widget has
- // not been made visible/sized yet. don't wrap until the widget size
- // is known.
- if (visualLineCount == 0 && width == 0) {
- return visualLineIndex;
- }
-
- GC gc = renderer.getGC();
- int numChars = Math.max(1, width / gc.getFontMetrics().getAverageCharWidth());
-
- for (int i = startLine; i < endLine; i++) {
- String line = logicalContent.getLine(i);
- int lineOffset = logicalContent.getOffsetAtLine(i);
- int lineLength = line.length();
-
- if (lineLength == 0) {
- setVisualLine(visualLineIndex, lineOffset, 0);
- visualLineIndex++;
- continue;
- }
- StyleRange[] styles = null;
- StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
- int startOffset = 0;
- int startX = 0;
-
- if (event != null) {
- styles = renderer.filterLineStyles(event.styles);
- }
- while (startOffset < lineLength) {
- int[] result = wrapLine(line, lineOffset, startOffset, startX, width, numChars, styles, gc);
-
- setVisualLine(visualLineIndex, lineOffset + startOffset, result[WRAP_LINE_LENGTH]);
- startOffset += result[WRAP_LINE_LENGTH];
- startX += result[WRAP_LINE_WIDTH];
- visualLineIndex++;
- }
- }
- renderer.disposeGC(gc);
- return visualLineIndex;
-}
-/**
- * Wrap all logical lines at the current client area width of the
- * StyledText widget
- */
-void wrapLines() {
- wrapLines(renderer.getClientArea().width);
-}
-/**
- * Wrap all logical lines at the given width.
- * <p>
- *
- * @param width width to wrap lines at
- */
-void wrapLines(int width) {
- int lineCount = logicalContent.getLineCount();
-
- visualLineCount = 0;
- visualLines = new int[lineCount][2];
- resetVisualLines(0, visualLines.length);
- wrapLineRange(0, lineCount, 0, width);
-}
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+
+/**
+ * An instance of class <code>WrappedContent</code> is used by
+ * StyledText to display wrapped lines. Lines are wrapped at word
+ * breaks which are marked by a space character. Trailing space
+ * behind words is kept on the current line.
+ * If the last remaining word on a line can not be fully displayed
+ * the line is wrapped character by character.
+ * WrappedContent wraps a StyledTextContent which provides the line
+ * data. The start offset and length of wrapped lines is calculated
+ * and updated based on recalculation requests and text changes.
+ * <p>
+ * All public methods in this class implement the
+ * <code>StyledTextContent</code> interface. Package visible
+ * methods are internal API for use by <code>StyledText</code>.
+ * </p>
+ */
+class WrappedContent implements StyledTextContent {
+ final static int LINE_OFFSET = 0; // index of line offset in visualLines array
+ final static int LINE_LENGTH = 1; // index of line lenght in visualLines array
+ final static int WRAP_LINE_LENGTH = 0;
+ final static int WRAP_LINE_WIDTH = 1;
+
+ StyledTextRenderer renderer;
+ StyledTextContent logicalContent;
+ int[][] visualLines; // start and length of each visual line
+ int visualLineCount = 0;
+
+/**
+ * Create a new instance.
+ *
+ * @param renderer <class>StyledTextRenderer</class> that renders
+ * the lines wrapped by the new instance.
+ * @param logicalContent StyledTextContent that provides the line
+ * data.
+ */
+WrappedContent(StyledTextRenderer renderer, StyledTextContent logicalContent) {
+ this.renderer = renderer;
+ this.logicalContent = logicalContent;
+}
+/**
+ * @see StyledTextContent#addTextChangeListener(TextChangeListener)
+ */
+public void addTextChangeListener(TextChangeListener listener) {
+ logicalContent.addTextChangeListener(listener);
+}
+/**
+ * Grow the lines array to at least the specified size.
+ * <p>
+ *
+ * @param numLines number of elements that the array should have
+ * at a minimum
+ */
+private void ensureSize(int numLines) {
+ int size = visualLines.length;
+ if (size >= numLines) {
+ return;
+ }
+ int[][] newLines = new int[Math.max(size * 2, numLines)][2];
+ System.arraycopy(visualLines, 0, newLines, 0, size);
+ visualLines = newLines;
+ resetVisualLines(size, visualLines.length - size);
+}
+/**
+ * @see StyledTextContent#getCharCount()
+ */
+public int getCharCount() {
+ return logicalContent.getCharCount();
+}
+/**
+ * @return the visual (wrapped) line at the specified index
+ * @see StyledTextContent#getLine(int)
+ */
+public String getLine(int lineIndex) {
+ String line;
+
+ // redirect call to logical content if there are no wrapped lines
+ if (visualLineCount == 0) {
+ line = logicalContent.getLine(lineIndex);
+ }
+ else {
+ if (lineIndex >= visualLineCount || lineIndex < 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ line = logicalContent.getTextRange(visualLines[lineIndex][LINE_OFFSET], visualLines[lineIndex][LINE_LENGTH]);
+ }
+ return line;
+}
+/**
+ * Returns the visual (wrapped) line at given offset.
+ * <p>
+ * The offset is ambiguous if it identifies the end of a visual line and
+ * there is another visual line below. In this case the end of the visual
+ * line has the same offset as the beginning of the next visual line
+ * since the visual line break is not represented by any character in the
+ * logical line.
+ * In this ambiguous case the offset is assumed to represent the end of a
+ * visual line and the index of the first visual line is returned.
+ * </p>
+ *
+ * @param offset offset of the desired line.
+ * @return the index of the visual (wrapped) line at the specified offset
+ * @see StyledTextContent#getLineAtOffset(int)
+ */
+public int getLineAtOffset(int offset) {
+ int lastLine = visualLineCount - 1;
+ int lastChar;
+
+ // redirect call to logical content if there are no wrapped lines
+ if (visualLineCount == 0) {
+ return logicalContent.getLineAtOffset(offset);
+ }
+ // can't use getCharCount to get the number of characters since this
+ // method is called in textChanged, when the logicalContent used by
+ // getCharCount has already changed. at that point the visual lines
+ // have not been updated yet and we thus need to use the old character
+ // count which is only available in the visual content.
+ lastChar = visualLines[lastLine][LINE_OFFSET] + visualLines[lastLine][LINE_LENGTH];
+ if (offset < 0 || (offset > 0 && offset > lastChar)) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ // if last line and the line is not empty you can ask for
+ // a position that doesn't exist (the one to the right of the
+ // last character) - for inserting
+ if (offset == lastChar) {
+ return lastLine;
+ }
+
+ int high = visualLineCount;
+ int low = -1;
+ int index = visualLineCount;
+ while (high - low > 1) {
+ index = (high + low) / 2;
+ int lineStart = visualLines[index][LINE_OFFSET];
+ if (offset >= lineStart) {
+ int lineEnd = lineStart + visualLines[index][LINE_LENGTH];
+ low = index;
+ if (offset <= lineEnd) {
+ break;
+ }
+ }
+ else {
+ high = index;
+ }
+ }
+ if (low > 0 && offset == visualLines[low - 1][LINE_OFFSET] + visualLines[low - 1][LINE_LENGTH]) {
+ // end of a visual line/beginning of next visual line is ambiguous
+ // (they have the same offset). always return the first visual line
+ low--;
+ }
+ return low;
+}
+/**
+ * @return the number of visual (wrapped) lines
+ * @see StyledTextContent#getLineCount()
+ */
+public int getLineCount() {
+ int lineCount = visualLineCount;
+
+ // redirect call to logical content if there are no wrapped lines
+ if (visualLineCount == 0) {
+ lineCount = logicalContent.getLineCount();
+ }
+ return lineCount;
+}
+/**
+ * @see StyledTextContent#getLineDelimiter()
+ */
+public String getLineDelimiter() {
+ return logicalContent.getLineDelimiter();
+}
+/**
+ * @return the start offset of the visual (wrapped) line at the given
+ * index
+ * @see StyledTextContent#getOffsetAtLine(int)
+ */
+public int getOffsetAtLine(int lineIndex) {
+ int offset;
+
+ // redirect call to logical content if there are no wrapped lines
+ if (visualLineCount == 0) {
+ offset = logicalContent.getOffsetAtLine(lineIndex);
+ }
+ else {
+ if (lineIndex >= visualLineCount || lineIndex < 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ offset = visualLines[lineIndex][LINE_OFFSET];
+ }
+ return offset;
+}
+/**
+ * @see StyledTextContent#getTextRange(int, int)
+ */
+public String getTextRange(int start, int length) {
+ return logicalContent.getTextRange(start, length);
+}
+/**
+ * Returns the number of visual (wrapped) lines.
+ *
+ * @return the number of visual (wrapped) lines
+ */
+int getVisualLineCount() {
+ return visualLineCount;
+}
+/**
+ * Returns the offset of the character after the word at the specified
+ * offset.
+ * <p>
+ * Words are separated by spaces. Trailing spaces are considered part
+ * of the word.
+ * </p>
+ *
+ * @param line logical line the word is in
+ * @param startOffset start offset of the line, relative to the start
+ * of the logical line.
+ * @param offset offset of the word to return the end of, relative to
+ * the start of the visual line.
+ * @return the offset of the character after the word at the specified
+ * offset.
+ */
+private int getWordEnd(String line, int startOffset, int offset) {
+ int lineLength = line.length();
+
+ offset += startOffset;
+ if (offset >= lineLength) {
+ return offset - startOffset;
+ }
+ // skip over leading whitespace
+ do {
+ offset++;
+ } while (offset < lineLength && Compatibility.isSpaceChar(line.charAt(offset)));
+ while (offset < lineLength && Compatibility.isSpaceChar(line.charAt(offset)) == false) {
+ offset++;
+ }
+ // skip over trailing whitespace
+ while (offset < lineLength && Compatibility.isSpaceChar(line.charAt(offset))) {
+ offset++;
+ }
+ return offset - startOffset;
+}
+/**
+ * Returns the start offset of the word at the specified offset.
+ * There are two classes of words formed by a sequence of characters:
+ * <p>
+ * Words are separated by spaces. Trailing spaces are considered part
+ * of the word.
+ * </p>
+ *
+ * @param line logical line the word is in
+ * @param startOffset start offset of the line, relative to the start
+ * of the logical line.
+ * @param offset offset of the word to return the start of, relative to
+ * the start of the visual line.
+ * @return the start offset of the word at the specified offset.
+ */
+private int getWordStart(String line, int startOffset, int offset) {
+ offset += startOffset;
+ // skip over trailing whitespace
+ do {
+ offset--;
+ } while (offset > startOffset && Compatibility.isSpaceChar(line.charAt(offset)));
+ while (offset > startOffset && Compatibility.isSpaceChar(line.charAt(offset - 1)) == false) {
+ offset--;
+ }
+ return offset - startOffset;
+}
+/**
+ * @see StyledTextContent#removeTextChangeListener(TextChangeListener)
+ */
+public void removeTextChangeListener(TextChangeListener listener) {
+ logicalContent.removeTextChangeListener(listener);
+}
+/**
+ * Reset the visual (wrapped) lines in the specified range.
+ * If the range specifies partial logical lines (e.g., startLine is
+ * the second of two visual lines) it is extended to reset all visual
+ * lines of a logical line.
+ * Following the reset the logical lines in the reset visual range are
+ * rewrapped.
+ * <p>
+ *
+ * @param startLine index of the first visual line
+ * @param lineCount number of visual lines
+ */
+void reset(int startLine, int lineCount) {
+ if (lineCount <= 0 || visualLineCount == 0) {
+ return;
+ }
+ reset(startLine, lineCount, true);
+}
+/**
+ * Reset the visual (wrapped) lines in the specified range.
+ * If the range specifies partial logical lines (e.g., startLine is
+ * the second of two visual lines) it is extended to reset all visual
+ * lines of a logical line.
+ * <p>
+ *
+ * @param startLine index of the first visual line
+ * @param lineCount number of visual lines
+ * @param wrap true=rewrap the logical lines in the reset visual range
+ * false=don't rewrap lines. Visual lines will be left in an inconsistent
+ * state since there will be a range of unwrapped and unknown lines.
+ * @return the first visual line that was reset
+ */
+private int reset(int startLine, int lineCount, boolean wrap) {
+ if (lineCount <= 0) {
+ return startLine;
+ }
+ // make sure that all visual lines of the first logical line are
+ // being reset. visualFirstLine is the first visual line of the
+ // first logical line that has at least one visual line reset.
+ int visualFirstLineOffset = getOffsetAtLine(startLine);
+ int logicalFirstLine = logicalContent.getLineAtOffset(visualFirstLineOffset);
+ int logicalFirstLineOffset = logicalContent.getOffsetAtLine(logicalFirstLine);
+ int visualFirstLine = getLineAtOffset(logicalFirstLineOffset);
+
+ lineCount += startLine - visualFirstLine;
+ startLine = visualFirstLine;
+
+ // make sure that all visual lines of the last logical line are
+ // being reset.
+ int lastLine = startLine + lineCount - 1;
+ int lastLineEnd = visualLines[lastLine][LINE_OFFSET] + visualLines[lastLine][LINE_LENGTH];
+ int logicalEndLine = 0;
+
+ while (lastLine < visualLineCount - 1 && lastLineEnd == visualLines[lastLine + 1][LINE_OFFSET]) {
+ lastLine++;
+ lastLineEnd = visualLines[lastLine][LINE_OFFSET] + visualLines[lastLine][LINE_LENGTH];
+ }
+ if (wrap) {
+ if (lastLine == visualLineCount - 1) {
+ logicalEndLine = logicalContent.getLineCount();
+ }
+ else {
+ logicalEndLine = logicalContent.getLineAtOffset(visualLines[lastLine + 1][LINE_OFFSET]);
+ }
+ }
+ lineCount = lastLine - startLine + 1;
+ resetVisualLines(startLine, lineCount);
+ visualLineCount -= lineCount;
+ if (wrap) {
+ // always recalculate line wrap immediately after a reset
+ // because the content always needs to be in a usable state.
+ // i.e., there must not be any reset but unwrapped lines
+ wrapLineRange(logicalFirstLine, logicalEndLine, startLine);
+ }
+ return startLine;
+}
+/**
+ * Reset the visual (wrapped) lines in the specified range.
+ * <p>
+ *
+ * @param startLine index of the first visual line
+ * @param lineCount number of visual lines
+ */
+private void resetVisualLines(int startLine, int lineCount) {
+ int endLine = startLine + lineCount;
+
+ for (int i = startLine; i < endLine; i++) {
+ visualLines[i] = new int[] {-1, -1};
+ }
+}
+/**
+ * @see StyledTextContent#replaceTextRange(int, int, String)
+ */
+public void replaceTextRange(int start, int replaceLength, String text) {
+ logicalContent.replaceTextRange(start, replaceLength, text);
+}
+/**
+ * @see StyledTextContent#setText(String)
+ */
+public void setText(String text) {
+ logicalContent.setText(text);
+}
+/**
+ * Set the line wrap data for the specified visual (wrapped) line.
+ * <p>
+ *
+ * @param visualLineIndex index of the visual line
+ * @param visualLineOffset start offset of the visual line, relative
+ * to the start of the document
+ * @param visualLineLength length of the visual line
+ */
+private void setVisualLine(int visualLineIndex, int visualLineOffset, int visualLineLength) {
+ ensureSize(visualLineCount + 1);
+ // is the space for the visual line already taken? can happen if
+ // there are more visual lines for a given logical line than before
+ if (visualLines[visualLineIndex][LINE_OFFSET] != -1) {
+ System.arraycopy(visualLines, visualLineIndex, visualLines, visualLineIndex + 1, visualLineCount - visualLineIndex);
+ visualLines[visualLineIndex] = new int[2];
+ }
+ visualLines[visualLineIndex][LINE_OFFSET] = visualLineOffset;
+ visualLines[visualLineIndex][LINE_LENGTH] = visualLineLength;
+ visualLineCount++;
+}
+/**
+ * Recalculates the line wrap for the lines affected by the
+ * text change.
+ * <p>
+ *
+ * @param startOffset the start offset of the text change
+ * @param newLineCount the number of inserted lines
+ * @param replaceLineCount the number of deleted lines
+ * @param newCharCount the number of new characters
+ * @param replaceCharCount the number of deleted characters
+ */
+void textChanged(int startOffset, int newLineCount, int replaceLineCount, int newCharCount, int replaceCharCount) {
+ // do nothing if there are no wrapped lines
+ if (visualLineCount == 0) {
+ return;
+ }
+ int logicalStartLine = logicalContent.getLineAtOffset(startOffset);
+ int visualStartLine = getLineAtOffset(startOffset);
+ int visualReplaceLastLine = visualLineCount - 1;
+ int textChangeDelta = newCharCount - replaceCharCount;
+
+ if (replaceLineCount > 0) {
+ visualReplaceLastLine = getLineAtOffset(startOffset + replaceCharCount);
+ // at the start of a visual line/end of the previous visual line?
+ if ((visualReplaceLastLine == 0 ||
+ visualLines[visualReplaceLastLine][LINE_OFFSET] == visualLines[visualReplaceLastLine - 1][LINE_OFFSET] + visualLines[visualReplaceLastLine - 1][LINE_LENGTH]) &&
+ visualReplaceLastLine != visualLineCount - 1) {
+ visualReplaceLastLine++;
+ }
+ visualStartLine = reset(visualStartLine, visualReplaceLastLine - visualStartLine + 1, false);
+ }
+ else {
+ visualStartLine = reset(visualStartLine, 1, false);
+ }
+ visualReplaceLastLine = wrapLineRange(logicalStartLine, logicalStartLine + 1 + newLineCount, visualStartLine);
+ for (int i = visualReplaceLastLine; i < visualLineCount; i++) {
+ visualLines[i][LINE_OFFSET] += textChangeDelta;
+ }
+}
+/**
+ * Measure the width of a segment in the specified logical line.
+ * <p>
+ *
+ * @param line the logical line
+ * @param logicalLineOffset start offset of the logical line, relative
+ * to the start of the document
+ * @param visualLineOffset offset to start measuring at/start offset
+ * of the visual line
+ * @param visualLineLength length of the segment to measure/the visual
+ * line
+ * @param styles StyleRanges to use during measuring
+ * @param startX x position of the visual line relative to the start
+ * of the logical line
+ * @param gc GC to use for measuring
+ */
+private int getTextWidth(String line, int logicalLineOffset, int visualLineOffset, int visualLineLength, StyleRange[] styles, int startX, GC gc) {
+ int width;
+
+ if (styles != null) {
+ // while wrapping a line, the logcial line styles may contain
+ // style ranges that don't apply (i.e., completely on the previous/next
+ // visual line). Therefore we need to filter the logical lines.
+ styles = renderer.getVisualLineStyleData(styles, logicalLineOffset + visualLineOffset, visualLineLength);
+ }
+ if (renderer.isBidi()) {
+ String wrappedLine = line.substring(visualLineOffset, visualLineOffset + visualLineLength);
+ StyledTextBidi bidi = renderer.getStyledTextBidi(wrappedLine, logicalLineOffset + visualLineOffset, gc, styles);
+ width = bidi.getTextWidth();
+ }
+ else {
+ width = renderer.getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
+ }
+ return width;
+}
+/**
+ * Wrap the given logical line at the specified offset.
+ * Called repeatedly until the entire logical lines has been split into
+ * visual (wrapped) lines.
+ * <p>
+ *
+ * @param line the logical line
+ * @param logicalLineOffset offset of the logical line, relative to the
+ * beginning of the content
+ * @param visualLineOffset start offset of the new visual line, relative
+ * to the start of the logical line.
+ * @param startX x position of visualLineOffset, relative to the beginning
+ * of the logical line
+ * @param width width to wrap the line to
+ * @param numChars average number of characters that fit into width
+ * @param styles StyleRanges to use for measuring the wrapped line
+ * @param gc GC to use for measuring
+ * @return int[0]=length of the new visual line, int[1]=width in pixels of
+ * the new visual line
+ */
+private int[] wrapLine(String line, int logicalLineOffset, int visualLineOffset, int startX, int width, int numChars, StyleRange[] styles, GC gc) {
+ int lineLength = line.length();
+ int lineWidth = 0;
+ int visualLineLength;
+
+ numChars = Math.min(numChars, lineLength - visualLineOffset);
+ visualLineLength = getWordStart(line, visualLineOffset, numChars);
+ // find a word that is within the client area. make sure at least one
+ // character is on each line so that line wrap algorithm terminates.
+ if (visualLineLength > 0) {
+ lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
+ if (lineWidth >= width) {
+ while (visualLineLength > 1 && lineWidth >= width) {
+ visualLineLength = getWordStart(line, visualLineOffset, visualLineLength);
+ lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
+ }
+ }
+ else
+ if (lineWidth < width) {
+ while (visualLineOffset + visualLineLength < lineLength) {
+ int newLineLength = getWordEnd(line, visualLineOffset, visualLineLength);
+ int newLineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, newLineLength, styles, startX, gc);
+ // would next word be beyond client area?
+ if (newLineWidth >= width) {
+ break;
+ }
+ else {
+ visualLineLength = newLineLength;
+ lineWidth = newLineWidth;
+ }
+ }
+ }
+ }
+ if (visualLineLength <= 0) {
+ // no complete word fits on the line. either first word was not within
+ // estimated number of characters or it was beyond the line width even
+ // though it was within numChars.
+ visualLineLength = numChars;
+ lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
+ if (lineWidth >= width) {
+ while (visualLineLength > 1 && lineWidth >= width) {
+ visualLineLength--;
+ lineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength, styles, startX, gc);
+ }
+ }
+ else
+ if (lineWidth < width) {
+ while (visualLineOffset + visualLineLength < lineLength) {
+ int newLineWidth = getTextWidth(line, logicalLineOffset, visualLineOffset, visualLineLength + 1, styles, startX, gc);
+ if (newLineWidth >= width) {
+ break;
+ }
+ else {
+ visualLineLength++;
+ lineWidth = newLineWidth;
+ }
+ }
+ }
+ }
+ return new int[] {visualLineLength, lineWidth};
+}
+/**
+ * Wrap the logical lines in the given range at the current client
+ * area width of the StyledText widget
+ * <p>
+ *
+ * @param startLine first logical line to wrap
+ * @param endLine line after last logical line
+ * @param visualLineIndex visual (wrapped) line index that startLine
+ * corresponds to.
+ * @return index of the line following the last wrapped line
+ */
+private int wrapLineRange(int startLine, int endLine, int visualLineIndex) {
+ int emptyLineCount = 0;
+
+ visualLineIndex = wrapLineRange(startLine, endLine, visualLineIndex, renderer.getClientArea().width);
+ // is there space left for more visual lines? can happen if there are fewer
+ // visual lines for a given logical line than before
+ for (int i = visualLineIndex; i < visualLines.length; i++, emptyLineCount++) {
+ if (visualLines[i][LINE_OFFSET] != -1) {
+ break;
+ }
+ }
+ if (emptyLineCount > 0) {
+ int copyLineCount = visualLineCount - visualLineIndex;
+ System.arraycopy(visualLines, visualLineIndex + emptyLineCount, visualLines, visualLineIndex, copyLineCount);
+ resetVisualLines(visualLineIndex + copyLineCount, emptyLineCount);
+ }
+ return visualLineIndex;
+}
+/**
+ * Wrap the lines in the given range. Skip lines that have already
+ * been wrapped.
+ * <p>
+ *
+ * @param startLine first logical line to wrap
+ * @param endLine line after last logical line
+ * @param visualLineIndex visual (wrapped) line index that startLine
+ * corresponds to.
+ * @param width line width to wrap at
+ * @return index of last wrapped line
+ */
+private int wrapLineRange(int startLine, int endLine, int visualLineIndex, int width) {
+ // if there are no wrapped lines and the width is 0 the widget has
+ // not been made visible/sized yet. don't wrap until the widget size
+ // is known.
+ if (visualLineCount == 0 && width == 0) {
+ return visualLineIndex;
+ }
+
+ GC gc = renderer.getGC();
+ int numChars = Math.max(1, width / gc.getFontMetrics().getAverageCharWidth());
+
+ for (int i = startLine; i < endLine; i++) {
+ String line = logicalContent.getLine(i);
+ int lineOffset = logicalContent.getOffsetAtLine(i);
+ int lineLength = line.length();
+
+ if (lineLength == 0) {
+ setVisualLine(visualLineIndex, lineOffset, 0);
+ visualLineIndex++;
+ continue;
+ }
+ StyleRange[] styles = null;
+ StyledTextEvent event = renderer.getLineStyleData(lineOffset, line);
+ int startOffset = 0;
+ int startX = 0;
+
+ if (event != null) {
+ styles = renderer.filterLineStyles(event.styles);
+ }
+ while (startOffset < lineLength) {
+ int[] result = wrapLine(line, lineOffset, startOffset, startX, width, numChars, styles, gc);
+
+ setVisualLine(visualLineIndex, lineOffset + startOffset, result[WRAP_LINE_LENGTH]);
+ startOffset += result[WRAP_LINE_LENGTH];
+ startX += result[WRAP_LINE_WIDTH];
+ visualLineIndex++;
+ }
+ }
+ renderer.disposeGC(gc);
+ return visualLineIndex;
+}
+/**
+ * Wrap all logical lines at the current client area width of the
+ * StyledText widget
+ */
+void wrapLines() {
+ wrapLines(renderer.getClientArea().width);
+}
+/**
+ * Wrap all logical lines at the given width.
+ * <p>
+ *
+ * @param width width to wrap lines at
+ */
+void wrapLines(int width) {
+ int lineCount = logicalContent.getLineCount();
+
+ visualLineCount = 0;
+ visualLines = new int[lineCount][2];
+ resetVisualLines(0, visualLines.length);
+ wrapLineRange(0, lineCount, 0, width);
+}
} \ No newline at end of file