Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Wilson2001-09-27 20:30:33 +0000
committerMike Wilson2001-09-27 20:30:33 +0000
commit91ec6adcd3e70cea1220ca541645cf57ce8d34da (patch)
treeb39c7eafab72d1583b6cf65660eaf10c2cb2a9c1
parent52b6af8a2028db6731dee8dd562517a9e1b4a67e (diff)
downloadeclipse.platform.swt-91ec6adcd3e70cea1220ca541645cf57ce8d34da.tar.gz
eclipse.platform.swt-91ec6adcd3e70cea1220ca541645cf57ce8d34da.tar.xz
eclipse.platform.swt-91ec6adcd3e70cea1220ca541645cf57ce8d34da.zip
*** empty log message ***
-rwxr-xr-xbundles/org.eclipse.swt/.classpath_motif1
-rwxr-xr-xbundles/org.eclipse.swt/.classpath_photon1
-rwxr-xr-xbundles/org.eclipse.swt/.cvsignore1
-rwxr-xr-xbundles/org.eclipse.swt/.vcm_meta1
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT AWT/win32/org/eclipse/swt/internal/awt/win32/SWT_AWT.java19
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/AnimatedProgress.java168
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/BusyIndicator.java71
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CCombo.java1067
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CLabel.java448
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolder.java1517
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderAdapter.java10
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderEvent.java18
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderListener.java12
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabItem.java436
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ControlEditor.java237
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultContent.java878
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultLineStyler.java625
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ExtendedModifyEvent.java23
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ExtendedModifyListener.java21
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineBackgroundEvent.java25
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineBackgroundListener.java23
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineStyleEvent.java24
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineStyleListener.java20
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/PopupList.java248
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ST.java56
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/SashForm.java397
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ScrolledComposite.java378
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StackLayout.java74
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyleRange.java146
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledText.java6418
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextBidi.java719
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextContent.java187
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextEvent.java30
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextListener.java65
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextPrinter.java352
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableEditor.java179
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableTree.java708
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableTreeEditor.java100
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableTreeItem.java558
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TextChangeListener.java43
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TextChangedEvent.java23
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TextChangingEvent.java41
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TreeEditor.java163
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/VerifyKeyListener.java18
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ViewForm.java491
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/close.gifbin0 -> 839 bytes
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/left.gifbin0 -> 828 bytes
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/leftDisabled.gifbin0 -> 828 bytes
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/package.html14
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/right.gifbin0 -> 828 bytes
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/rightDisabled.gifbin0 -> 828 bytes
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/motif/org/eclipse/swt/dnd/Clipboard.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/motif/org/eclipse/swt/dnd/Transfer.java1
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/ByteArrayTransfer.java12
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/Clipboard.java143
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/FileTransfer.java43
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/RTFTransfer.java23
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/TextTransfer.java23
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/Transfer.java3
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Drag and Drop/photon/org/eclipse/swt/dnd/TransferData.java5
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT OLE Win32/win32/org/eclipse/swt/ole/win32/OLE.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT OLE Win32/win32/org/eclipse/swt/ole/win32/OleClientSite.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT OLE Win32/win32/org/eclipse/swt/ole/win32/OleFrame.java3
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Printing/common/org/eclipse/swt/printing/PrinterData.java4
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Printing/motif/org/eclipse/swt/printing/Printer.java41
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Printing/photon/org/eclipse/swt/printing/Printer.java6
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Printing/win32/org/eclipse/swt/printing/PrintDialog.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Printing/win32/org/eclipse/swt/printing/Printer.java29
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Program/motif/org/eclipse/swt/program/Program.java1757
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Program/photon/org/eclipse/swt/program/Program.java41
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT Program/win32/org/eclipse/swt/program/Program.java48
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/library/callback.c109
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java9
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java3827
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java9
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Point.java4
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/RGB.java4
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/Rectangle.java9
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/Callback.java21
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Dialog.java3
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Item.java4
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/RunnableLock.java3
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Synchronizer.java16
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/common/version.txt2
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common_j2me/org/eclipse/swt/internal/Compatability.java26
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common_j2me/org/eclipse/swt/internal/EventObjectCompatability.java43
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common_j2me/org/eclipse/swt/internal/SerializableCompatability.java6
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common_j2se/org/eclipse/swt/internal/Compatability.java21
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common_j2se/org/eclipse/swt/internal/EventObjectCompatability.java5
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common_j2se/org/eclipse/swt/internal/SerializableCompatability.java6
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/AbstractTreeItem.java4
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Header.java1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/SelectableItemWidget.java11
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeRoots.java4
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/motif/java/util/EventListener.java5
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/library/build.csh52
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/library/kde.cc461
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/library/make_linux.mak17
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/library/swt.c679
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/graphics/Device.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/graphics/Font.java96
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/graphics/FontData.java26
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/graphics/GC.java241
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/graphics/Image.java179
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/graphics/Region.java7
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/internal/Converter.java438
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/internal/motif/KDE.java64
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/internal/motif/OS.java111
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Button.java367
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Canvas.java244
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Caret.java538
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/ColorDialog.java250
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Combo.java1147
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Composite.java243
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Control.java2798
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/CoolBar.java422
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/CoolItem.java342
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Decorations.java625
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/DirectoryDialog.java206
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Display.java1147
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/FileDialog.java308
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/FontDialog.java157
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Group.java139
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Label.java280
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/List.java1305
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Menu.java667
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/MenuItem.java616
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/MessageBox.java129
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/ProgressBar.java258
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Sash.java71
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Scale.java348
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/ScrollBar.java741
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Scrollable.java224
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Shell.java749
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Slider.java468
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Text.java1399
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/ToolBar.java309
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/ToolDrawable.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/ToolItem.java651
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Tracker.java286
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Widget.java756
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/library/makefile.mak7
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/library/structs.c166
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/library/structs.h43
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/library/swt.c390
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/graphics/Device.java18
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/graphics/FontData.java12
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/graphics/GC.java265
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/graphics/Image.java68
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/graphics/Region.java4
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/internal/photon/OS.java54
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/internal/photon/PhKeyEvent_t.java5
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Button.java75
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Canvas.java21
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Caret.java75
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/ColorDialog.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Combo.java110
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Composite.java163
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Control.java305
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Decorations.java66
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Display.java24
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/FileDialog.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/FontDialog.java2
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Group.java11
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Label.java33
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/List.java105
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Menu.java65
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/MenuItem.java163
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/ProgressBar.java23
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Sash.java11
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Scale.java40
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/ScrollBar.java69
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Scrollable.java12
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Shell.java168
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Slider.java51
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/TabFolder.java38
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/TabItem.java28
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Text.java179
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/ToolBar.java35
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/ToolItem.java86
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Tracker.java28
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/photon/org/eclipse/swt/widgets/Widget.java114
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/library/makefile.mak7
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/library/structs.c104
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/library/structs.h48
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/library/swt.c353
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/library/swt.rc6
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Cursor.java19
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Device.java1
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/FontData.java70
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java186
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java42
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Region.java5
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/win32/OS.java230
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java9
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Canvas.java10
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java51
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ColorDialog.java11
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Combo.java19
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java8
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java255
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolBar.java4
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/CoolItem.java15
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Decorations.java17
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/DirectoryDialog.java26
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java217
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FileDialog.java139
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/FontDialog.java78
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Group.java34
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ImageList.java98
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Label.java10
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/List.java57
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Menu.java12
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MenuItem.java72
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/MessageBox.java79
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ProgressBar.java28
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Sash.java28
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scale.java28
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ScrollBar.java79
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Scrollable.java8
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java86
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Slider.java39
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabFolder.java80
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TabItem.java72
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java100
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java67
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableItem.java82
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Text.java85
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java98
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java126
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java59
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tree.java113
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TreeItem.java140
-rwxr-xr-xbundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java57
-rwxr-xr-xbundles/org.eclipse.swt/buildnotes_swt.html177
-rwxr-xr-xbundles/org.eclipse.swt/plugin.xml6
-rwxr-xr-xbundles/org.eclipse.swt/readme_swt.html80
237 files changed, 37020 insertions, 14007 deletions
diff --git a/bundles/org.eclipse.swt/.classpath_motif b/bundles/org.eclipse.swt/.classpath_motif
index 9446ffe549..d89e2d31c2 100755
--- a/bundles/org.eclipse.swt/.classpath_motif
+++ b/bundles/org.eclipse.swt/.classpath_motif
@@ -2,6 +2,7 @@
<classpath>
<classpathentry kind="var" path="JRE_LIB"/>
<classpathentry kind="src" path="Eclipse SWT/motif"/>
+ <classpathentry kind="src" path="Eclipse SWT/emulated"/>
<classpathentry kind="src" path="Eclipse SWT/common"/>
<classpathentry kind="src" path="Eclipse SWT Printing/motif"/>
<classpathentry kind="src" path="Eclipse SWT Printing/common"/>
diff --git a/bundles/org.eclipse.swt/.classpath_photon b/bundles/org.eclipse.swt/.classpath_photon
index c061bdbf30..89aa3ff82e 100755
--- a/bundles/org.eclipse.swt/.classpath_photon
+++ b/bundles/org.eclipse.swt/.classpath_photon
@@ -2,6 +2,7 @@
<classpath>
<classpathentry kind="var" path="JRE_LIB"/>
<classpathentry kind="src" path="Eclipse SWT/photon"/>
+ <classpathentry kind="src" path="Eclipse SWT/emulated"/>
<classpathentry kind="src" path="Eclipse SWT/common"/>
<classpathentry kind="src" path="Eclipse SWT Printing/photon"/>
<classpathentry kind="src" path="Eclipse SWT Printing/common"/>
diff --git a/bundles/org.eclipse.swt/.cvsignore b/bundles/org.eclipse.swt/.cvsignore
index 30e8e7346e..3f041525eb 100755
--- a/bundles/org.eclipse.swt/.cvsignore
+++ b/bundles/org.eclipse.swt/.cvsignore
@@ -1,3 +1,2 @@
bin
.classpath
-build.properties \ No newline at end of file
diff --git a/bundles/org.eclipse.swt/.vcm_meta b/bundles/org.eclipse.swt/.vcm_meta
index 67f905f685..0c9e8d8b7a 100755
--- a/bundles/org.eclipse.swt/.vcm_meta
+++ b/bundles/org.eclipse.swt/.vcm_meta
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-description>
- <comment></comment>
<nature id="org.eclipse.jdt.core.javanature"/>
<nature id="org.eclipse.pde.PluginNature"/>
<builder name="org.eclipse.jdt.core.javabuilder">
diff --git a/bundles/org.eclipse.swt/Eclipse SWT AWT/win32/org/eclipse/swt/internal/awt/win32/SWT_AWT.java b/bundles/org.eclipse.swt/Eclipse SWT AWT/win32/org/eclipse/swt/internal/awt/win32/SWT_AWT.java
index d6777ad621..d6f26064d6 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT AWT/win32/org/eclipse/swt/internal/awt/win32/SWT_AWT.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT AWT/win32/org/eclipse/swt/internal/awt/win32/SWT_AWT.java
@@ -25,6 +25,8 @@ import java.awt.Panel;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.FocusEvent;
public class SWT_AWT {
@@ -33,6 +35,18 @@ public static Panel new_Panel (final Composite parent) {
final WEmbeddedFrame frame = new WEmbeddedFrame (handle);
Panel panel = new Panel ();
frame.add (panel);
+ parent.addListener (SWT.Activate, new Listener () {
+ public void handleEvent (Event e) {
+ frame.dispatchEvent (new WindowEvent (frame, WindowEvent.WINDOW_ACTIVATED));
+ frame.dispatchEvent (new FocusEvent (frame, FocusEvent.FOCUS_GAINED));
+ }
+ });
+ parent.addListener (SWT.Deactivate, new Listener () {
+ public void handleEvent (Event e) {
+ frame.dispatchEvent (new WindowEvent(frame, WindowEvent.WINDOW_DEACTIVATED));
+ frame.dispatchEvent (new FocusEvent(frame, FocusEvent.FOCUS_LOST));
+ }
+ });
parent.addListener (SWT.Resize, new Listener () {
public void handleEvent (Event e) {
Rectangle rect = parent.getClientArea ();
@@ -40,6 +54,11 @@ public static Panel new_Panel (final Composite parent) {
frame.validate ();
}
});
+ parent.addListener (SWT.Dispose, new Listener () {
+ public void handleEvent (Event e) {
+ frame.dispose ();
+ }
+ });
return panel;
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/AnimatedProgress.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/AnimatedProgress.java
new file mode 100644
index 0000000000..efdb04a943
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/AnimatedProgress.java
@@ -0,0 +1,168 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+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.
+ */
+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;
+
+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.
+ */
+public synchronized void clear(){
+ if (active)
+ stop();
+ showStripes = false;
+ redraw();
+}
+public Point computeSize(int wHint, int hHint, boolean changed) {
+ 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.
+*/
+public synchronized void start() {
+
+ 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() {
+ active = false;
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/BusyIndicator.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/BusyIndicator.java
new file mode 100644
index 0000000000..fc640a5a31
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/BusyIndicator.java
@@ -0,0 +1,71 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+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";
+
+/**
+ * Runs the given <code>Runnable</code> while providing
+ * busy feedback using this busy indicator.
+ *
+ * @param 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 the runnable for which busy feedback is to be shown
+ * @see #showWhile
+ */
+
+public static void showWhile(Display display, Runnable runnable) {
+ 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_j2se/org/eclipse/swt/custom/CCombo.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CCombo.java
new file mode 100644
index 0000000000..3bd86c4826
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CCombo.java
@@ -0,0 +1,1067 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * 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</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;
+
+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.MouseDown, SWT.FocusIn, SWT.FocusOut};
+ for (int i=0; i<arrowEvents.length; i++) arrow.addListener (arrowEvents [i], listener);
+
+}
+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) {
+ 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) {
+ 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) {;
+ 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) {
+ 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: {
+ Control focusControl = getDisplay ().getFocusControl();
+ if (focusControl == list || focusControl == text) return;
+ hasFocus = false;
+ Event e = new Event();
+ e.time = event.time;
+ notifyListeners(SWT.FocusOut, e);
+ break;
+ }
+ case SWT.MouseDown: {
+ if (event.button != 1) return;
+ 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 () {
+ 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) {
+ 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);
+ Point barSize = list.getVerticalBar().getSize();
+ 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) {
+ 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 () {
+ 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 ();
+ int borderWidth = getBorderWidth();
+ Point point = toDisplay (new Point (0 - borderWidth, 0 - borderWidth));
+ Point comboSize = getSize();
+ popup.setBounds (point.x, point.y + comboSize.y, comboSize.x, listRect.height + 2);
+ popup.setVisible (true);
+ list.setFocus();
+}
+public Control [] getChildren () {
+ 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) {
+ 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 () {
+ 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 () {
+ 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 () {
+ 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 () {
+ 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 () {
+ 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 () {
+ 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 () {
+ 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 () {
+ 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) {
+ 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) {
+ if (string == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return list.indexOf (string, start);
+}
+boolean isDropped () {
+ return popup.getVisible ();
+}
+public boolean isFocusControl () {
+ 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 listHeight = list.getItemHeight () * ITEMS_SHOWING;
+ Rectangle trim = list.computeTrim (0, 0, size.x - 2, listHeight);
+ list.setBounds (1, 1, size.x - 2, trim.height);
+}
+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: {
+ Control focusControl = getDisplay ().getFocusControl();
+ if (focusControl == text || focusControl == arrow) return;
+ hasFocus = false;
+ Event e = new Event();
+ e.time = event.time;
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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 () {
+ text.setText ("");
+ 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) {
+ 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) {
+ 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) {
+ if (index == -1) {
+ list.deselectAll ();
+ text.setText ("");
+ 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);
+}
+
+
+/**
+* Sets the focus.
+*/
+public boolean setFocus () {
+ return text.setFocus ();
+}
+/**
+* Sets the widget font.
+*/
+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) {
+ 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) {
+ if (items == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ int style = getStyle();
+ if ((style & SWT.READ_ONLY) != 0) text.setText ("");
+ 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) {
+ 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) {
+ 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) {
+ text.setTextLimit (limit);
+}
+public void setVisible (boolean visible) {
+ super.setVisible(visible);
+ if (!getVisible()) {
+ Composite parent = getParent();
+ while (parent != null && !parent.isVisible()) {
+ parent = parent.getParent();
+ }
+ if (parent != null) parent.setFocus();
+ }
+}
+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: {
+ Control focusControl = getDisplay ().getFocusControl();
+ if (focusControl == list || focusControl == arrow) return;
+ hasFocus = false;
+ Event e = new Event();
+ e.time = event.time;
+ 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 || event.character == '\t') {
+ 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_TAB_NEXT:
+ case SWT.TRAVERSE_ARROW_PREVIOUS:
+ case SWT.TRAVERSE_ARROW_NEXT:
+ // The tab key 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_j2se/org/eclipse/swt/custom/CLabel.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CLabel.java
new file mode 100644
index 0000000000..799891d8fa
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CLabel.java
@@ -0,0 +1,448 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.events.*;
+
+/**
+ * A Label which supports aligned text and/or an image and different border styles.
+ * <p>
+ * If there is not enough space a SmartLabel 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>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 = "...";
+ /** 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;
+
+
+/**
+ * Create a CLabel with the given border style as a child of parent.
+ */
+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);
+ }
+ });
+
+}
+/**
+ * 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 | SWT.NO_BACKGROUND;
+ return style;
+}
+public Point computeSize(int wHint, int hHint, boolean changed) {
+ 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.
+ */
+public int getAlignment() {
+ return align;
+}
+/**
+ * Return the CLabel's image or <code>null</code>.
+ */
+public Image getImage() {
+ 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.
+ */
+public String getText() {
+ return text;
+}
+public String getToolTipText () {
+ 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 onDispose(DisposeEvent event) {
+ gradientColors = null;
+ gradientPercents = null;
+ backgroundImage = 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 {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(rect);
+ }
+ } catch (SWTException e) {
+ 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.
+ */
+public void setAlignment(int align) {
+ if (align != SWT.LEFT && align != SWT.RIGHT && align != SWT.CENTER) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (this.align != align) {
+ this.align = align;
+ redraw();
+ }
+}
+/**
+ * Specify a gradiant of colours to be draw in the background of the CLabel.
+ * For example to draw a gradiant that varies from dark blue to blue and then to
+ * white, use the following call to setBackground:
+ * <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 gradiant
+ * in order of appearance left to right. The value <code>null</code> clears the
+ * background gradiant. 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.
+ */
+public void setBackground(Color[] colors, int[] percents) {
+ 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();
+}
+public void setBackground(Image image) {
+ if (image == backgroundImage) return;
+ 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.
+ */
+public void setImage(Image image) {
+ if (image != this.image) {
+ this.image = image;
+ redraw();
+ }
+}
+/**
+ * Set the label's text.
+ * The value <code>null</code> clears it.
+ */
+public void setText(String text) {
+ if (text == null) text = "";
+ 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_j2se/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolder.java
new file mode 100644
index 0000000000..e99057514b
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolder.java
@@ -0,0 +1,1517 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.layout.*;
+
+/**
+ * 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>
+ * 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 static int MIN_TAB_WIDTH = 3;
+
+ /* sizing, positioning */
+ int xClient, yClient;
+ boolean onBottom = false;
+ int fixedTabHeight = -1;
+ int imageHeight = -1;
+
+ /* 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;
+
+ // internal constants
+ private static final int DEFAULT_WIDTH = 64;
+ private static final int DEFAULT_HEIGHT = 64;
+
+ // scrolling arrows
+ private ToolBar scrollBar;
+ private ToolItem scrollLeft;
+ private ToolItem scrollRight;
+ private Image arrowLeftImage;
+ private Image arrowRightImage;
+ private Image arrowLeftDisabledImage;
+ private Image arrowRightDisabledImage;
+
+ // close button
+ boolean showClose = false;
+ private Image closeImage;
+ ToolBar closeBar;
+ private ToolItem closeItem;
+ private ToolBar inactiveCloseBar;
+ private ToolItem inactiveCloseItem;
+ private CTabItem inactiveItem;
+
+ private boolean shortenedTabs = false;
+
+ // borders
+ boolean showBorders = false;
+ private int BORDER_BOTTOM = 0;
+ private int BORDER_LEFT = 0;
+ private int BORDER_RIGHT = 0;
+ private int BORDER_TOP = 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 Rectangle oldArea;
+
+ // 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 boolean inDoubleClick = false;
+
+/**
+ * Construct a CTabFolder with the specified parent and style.
+ * @param parent org.eclipse.swt.widgets.Composite
+ * @param swtStyle int
+ */
+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);
+ Color background = getBackground();
+
+ // create scrolling arrow buttons
+ scrollBar = new ToolBar(this, SWT.FLAT);
+ scrollBar.setVisible(false);
+ scrollBar.setBackground(background);
+ scrollLeft = new ToolItem(scrollBar, SWT.PUSH);
+ scrollLeft.setEnabled(false);
+ scrollRight = new ToolItem(scrollBar, SWT.PUSH);
+ scrollRight.setEnabled(false);
+
+ // create close buttons
+ closeBar = new ToolBar(this, SWT.FLAT);
+ closeBar.setVisible(false);
+ closeBar.setBackground(background);
+ closeItem = new ToolItem(closeBar, SWT.PUSH);
+
+ inactiveCloseBar = new ToolBar(this, SWT.FLAT);
+ inactiveCloseBar.setVisible(false);
+ inactiveCloseBar.setBackground(background);
+ inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH);
+
+ Listener listener = new Listener() {
+ public void handleEvent(Event event) {
+ handleEvents(event);
+ }
+ };
+ addListener(SWT.Dispose, listener);
+ addListener(SWT.MouseUp, listener);
+ addListener(SWT.MouseDoubleClick, listener);
+ addListener(SWT.MouseMove, listener);
+ addListener(SWT.MouseExit, listener);
+ addListener(SWT.Paint, listener);
+ addListener(SWT.Resize, listener);
+ scrollLeft.addListener(SWT.Selection, listener);
+ scrollRight.addListener(SWT.Selection, listener);
+ closeItem.addListener(SWT.Selection, listener);
+ inactiveCloseItem.addListener(SWT.Selection, listener);
+ inactiveCloseBar.addListener (SWT.MouseExit, listener);
+
+ setBorderVisible((style & SWT.BORDER) != 0);
+
+ // tool tip support
+ Display display = getDisplay();
+ tip = new Shell (getShell(), SWT.ON_TOP);
+ GridLayout layout = new GridLayout();
+ layout.marginWidth = layout.marginHeight = 1;
+ tip.setLayout(layout);
+ Label label = new Label (tip, SWT.NONE);
+ label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ label.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
+ label.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
+ tip.setBackground(label.getBackground());
+
+ addMouseTrackListener (new MouseTrackAdapter () {
+ public void mouseExit(MouseEvent e) {
+ if (tip.isDisposed()) return;
+ if (tip.isVisible()) tip.setVisible(false);
+ }
+ public void mouseHover(MouseEvent e) {
+ if (tip.isDisposed()) return;
+ Point pt = new Point (e.x, e.y);
+ CTabItem item = getItem(pt);
+ if (item != null) {
+ String tooltip = item.getToolTipText();
+ if (tooltip != null) {
+
+ Label label = (Label) (tip.getChildren() [0]);
+ label.setText(tooltip);
+ tip.pack();
+
+ pt.y += 16;
+ pt = toDisplay(pt);
+ /*
+ * Ensure that the tooltip is on the screen.
+ */
+ Display display = tip.getDisplay();
+ Rectangle rect = display.getBounds();
+ Point size = tip.getSize();
+ pt.x = Math.max (0, Math.min (pt.x, rect.width - size.x));
+ pt.y = Math.max (0, Math.min (pt.y, rect.height - size.y));
+ tip.setLocation(pt);
+
+ tip.setVisible(true);
+ return;
+ }
+ }
+
+ tip.setVisible(false);
+ }
+ });
+
+}
+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) {
+ 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_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see CTabFolderListener
+ * @see #removeCTabFolderListener
+ */
+public void addCTabFolderListener(CTabFolderListener listener) {
+ 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;
+ initCloseButtonImages();
+}
+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) {
+ int minWidth = 0;
+ int minHeight = 0;
+
+ // tab width
+ if (items.length > 0) {
+ CTabItem lastItem = items[items.length-1];
+ minWidth = lastItem.x + lastItem.width;
+ }
+
+ // get max preferred size of 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) {
+ int tabHeight = getTabHeight();
+ int trimX = x - marginWidth - BORDER_LEFT;
+ int trimY = y - marginHeight - tabHeight - BORDER_TOP;
+ if (onBottom) {
+ trimY = y - marginHeight - BORDER_TOP;
+ }
+ int trimWidth = width + BORDER_LEFT + BORDER_RIGHT + 2*marginWidth;
+ int trimHeight = height + BORDER_TOP + BORDER_BOTTOM + 2*marginHeight + tabHeight;
+ 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;
+ }
+
+ layoutItems();
+ ensureVisible();
+
+ if (items.length == 1) {
+ redraw();
+ } else {
+ redrawTabArea(-1);
+ }
+}
+/**
+ * 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);
+ 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;
+ setSelectionNotify(Math.max(0, index - 1));
+ } else if (selectedIndex > index) {
+ selectedIndex --;
+ }
+
+ layoutItems();
+ ensureVisible();
+ redrawTabArea(-1);
+}
+/**
+ * Dispose the items of the receiver
+ */
+private void onDispose() {
+ inDispose = true;
+
+ // items array is resized during CTabItem.dispose
+ // it is set to null if the last item is removed
+ 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;
+ }
+
+ gradientColors = null;
+ gradientPercents = null;
+ backgroundImage = null;
+
+ if (arrowLeftImage != null){
+ arrowLeftImage.dispose();
+ arrowLeftImage = null;
+ }
+
+ if (arrowRightImage != null){
+ arrowRightImage.dispose();
+ arrowRightImage = null;
+ }
+
+ if (arrowLeftDisabledImage != null) {
+ arrowLeftDisabledImage.dispose();
+ arrowLeftDisabledImage = null;
+ }
+
+ if (arrowRightDisabledImage != null) {
+ arrowRightDisabledImage.dispose();
+ arrowRightDisabledImage = null;
+ }
+
+ if (closeImage != null) {
+ closeImage.dispose();
+ closeImage = null;
+ }
+
+ if (borderColor1 != null) {
+ borderColor1.dispose();
+ borderColor1 = null;
+ }
+
+ if (borderColor2 != null) {
+ borderColor2.dispose();
+ borderColor2 = null;
+ }
+
+ if (borderColor3 != null) {
+ borderColor3.dispose();
+ borderColor3 = null;
+ }
+}
+/**
+ * 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 tabHeight = getTabHeight();
+ int lineY = d.y + BORDER_TOP + tabHeight;
+ if (onBottom) {
+ lineY = d.y + d.height - BORDER_BOTTOM - tabHeight - 1;
+ }
+ gc.setForeground(borderColor1);
+ gc.drawLine(d.x + BORDER_LEFT, lineY, d.x + d.width - BORDER_RIGHT, lineY);
+ }
+
+ gc.setForeground(getForeground());
+}
+public Rectangle getClientArea() {
+ Rectangle clientArea = super.getClientArea();
+ clientArea.x = xClient;
+ clientArea.y = yClient;
+ clientArea.width -= 2*marginWidth + BORDER_LEFT + BORDER_RIGHT;
+ clientArea.height -= 2*marginHeight + BORDER_TOP + BORDER_BOTTOM + getTabHeight() + 1;
+ return clientArea;
+}
+/**
+ * Return the height of item images. All images are scaled to
+ * the height of the first image.
+ */
+int getImageHeight() {
+ return imageHeight;
+}
+public int getTabHeight(){
+ if (fixedTabHeight > 0) return fixedTabHeight;
+
+ if (isDisposed()) return 0;
+
+ int tempHeight = 0;
+ GC gc = new GC(this);
+ for (int i=0; i < items.length; i++) {
+ int height = items[i].preferredHeight(gc);
+ tempHeight = Math.max(tempHeight, height);
+ }
+ gc.dispose();
+ return tempHeight;
+}
+/**
+ * Return the tab that is located at the specified index.
+ */
+public CTabItem getItem (int index) {
+ 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
+*
+* @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 CTabItem getItem (Point pt) {
+ for (int i = 0; i < items.length; i++) {
+ Rectangle bounds = items[i].getBounds();
+ if (bounds.contains(pt)) return items[i];
+ }
+ return null;
+}
+/**
+ * Return the number of tabs in the folder.
+ */
+public int getItemCount(){
+ return items.length;
+}
+/**
+ * Return the tab items.
+ */
+public CTabItem [] getItems() {
+ CTabItem[] tabItems = new CTabItem [items.length];
+ System.arraycopy(items, 0, tabItems, 0, items.length);
+ return tabItems;
+}
+/**
+ * Return the selected tab item, or an empty array if there
+ * is no selection.
+ */
+public CTabItem getSelection() {
+ if (selectedIndex == -1) return null;
+ return items[selectedIndex];
+}
+/**
+ * Return the index of the selected tab item, or -1 if there
+ * is no selection.
+ */
+public int getSelectionIndex() {
+ return selectedIndex;
+}
+
+private void handleEvents (Event event){
+ switch (event.type) {
+ case SWT.Dispose:
+ onDispose();
+ break;
+ case SWT.Paint:
+ onPaint(event);
+ break;
+ case SWT.Resize:
+ onResize();
+ break;
+ case SWT.MouseDoubleClick:
+ inDoubleClick = true;
+ break;
+ case SWT.MouseUp:
+ onMouseUp(event);
+ break;
+ case SWT.MouseExit:
+ if (event.widget == this) {
+ Rectangle inactiveBounds = inactiveCloseBar.getBounds();
+ if (inactiveBounds.contains(event.x, event.y)) return;
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+ }
+ if (event.widget == inactiveCloseBar) {
+ if (inactiveItem != null) {
+ Rectangle itemBounds = inactiveItem.getBounds();
+ if (itemBounds.contains(event.x, event.y)) return;
+ }
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+ }
+ break;
+ case SWT.MouseMove:
+ onMouseMove(event);
+ break;
+ case SWT.Selection:
+ if (event.widget == scrollLeft) {
+ scroll_scrollLeft();
+ }
+ if (event.widget == scrollRight) {
+ scroll_scrollRight();
+ }
+ if (event.widget == closeItem) {
+ closeNotify(getSelection(), event.time);
+ }
+ if (event.widget == inactiveCloseItem) {
+ closeNotify(inactiveItem, event.time);
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+ }
+ break;
+ default:
+ break;
+ }
+}
+/**
+ * Return the index of the specified tab or -1 if the tab is not
+ * in the receiver.
+ */
+public int indexOf(CTabItem item) {
+ 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;
+}
+/**
+ * 'item' has changed. Store the image size if this is the
+ * first item with an image.
+ */
+void itemChanged(CTabItem item) {
+ Image itemImage = item.getImage();
+ if (imageHeight == -1 && itemImage != null) {
+ imageHeight = itemImage.getBounds().height;
+ }
+
+ int height = getTabHeight();
+ Point size = scrollBar.computeSize(SWT.DEFAULT, height);
+ scrollBar.setSize(size);
+ height = (onBottom) ? height - 2 : height - 1;
+ size = closeBar.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ closeBar.setSize(size);
+ inactiveCloseBar.setSize(size);
+
+ layoutItems();
+ redrawTabArea(-1);
+}
+private void layoutButtons() {
+
+ boolean leftVisible = scroll_leftVisible();
+ boolean rightVisible = scroll_rightVisible();
+
+ if (leftVisible || rightVisible) {
+ initScrollBarImages();
+ Point size = scrollBar.getSize();
+ Rectangle area = super.getClientArea();
+ int x = area.x + area.width - size.x - BORDER_RIGHT;
+ int y = 0;
+ if (!onBottom) {
+ y = area.y + BORDER_TOP;
+ } else {
+ y = area.y + area.height - BORDER_BOTTOM - size.y;
+ }
+ scrollBar.setLocation(x, y);
+ scrollLeft.setEnabled(leftVisible);
+ scrollRight.setEnabled(rightVisible);
+ scrollBar.setVisible(true);
+ } else {
+ scrollBar.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) {
+ Point size = closeBar.getSize();
+ CTabItem item = getSelection();
+ if (item == null) {
+ closeBar.setVisible(false);
+ } else {
+ int x = item.x + item.width - size.x - 2;
+ int y = item.y + 1 + (item.height - 2 - size.y) / 2; // +1 for white line across top of tab
+ closeBar.setLocation(x, y);
+ if (scrollBar.isVisible()) {
+ Rectangle scrollRect = scrollBar.getBounds();
+ scrollRect.width += BORDER_RIGHT;
+ closeBar.setVisible(!scrollRect.contains(x, y));
+ } else {
+ closeBar.setVisible(true);
+ }
+ }
+ }
+}
+/**
+ * Layout the items and store the client area size.
+ */
+private void layoutItems() {
+ if (isDisposed()) return;
+
+ Rectangle area = super.getClientArea();
+ int tabHeight = getTabHeight();
+
+ shortenedTabs = false;
+ if (items.length > 0) {
+ int[] widths = new int[items.length];
+ int totalWidth = 0;
+ GC gc = new GC(this);
+ for (int i = 0; i < items.length; i++) {
+ widths[i] = items[i].preferredWidth(gc);
+ totalWidth += widths[i];
+ }
+ gc.dispose();
+ if (totalWidth < (area.width - BORDER_LEFT - BORDER_RIGHT) ) {
+ topTabIndex = 0;
+ } else {
+
+ int oldAverageWidth = 0;
+ int averageWidth = (area.width - BORDER_LEFT - BORDER_RIGHT) / items.length;
+ while (averageWidth > oldAverageWidth) {
+ int width = area.width - BORDER_LEFT - BORDER_RIGHT;
+ 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;
+ }
+ }
+ if (averageWidth > MIN_TAB_WIDTH * tabHeight) {
+ for (int i = 0; i < items.length; i++) {
+ if (widths[i] > averageWidth) {
+ widths[i] = averageWidth;
+ }
+ }
+ topTabIndex = 0;
+ shortenedTabs = true;
+ }
+ }
+ int x = area.x;
+ int y = area.y + BORDER_TOP;
+ if (onBottom) {
+ y = Math.max(0, area.y + area.height - BORDER_BOTTOM - tabHeight);
+ }
+ for (int i = topTabIndex - 1; i>=0; i--) {
+ // if the first visible tab is not the first tab
+ CTabItem tab = items[i];
+ tab.width = widths[i];
+ tab.height = getTabHeight();
+ x -= tab.width;
+ // layout tab items from right to left thus making them invisible
+ tab.x = x;
+ tab.y = y;
+ }
+
+ x = area.x + BORDER_LEFT;
+ 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;
+ tab.height = tabHeight;
+ tab.width = widths[i];
+ x = x + tab.width;
+ }
+ }
+
+ xClient = area.x + BORDER_LEFT + marginWidth;
+ if (onBottom) {
+ yClient = area.y + BORDER_TOP + marginHeight;
+ } else {
+ yClient = area.y + BORDER_TOP + tabHeight + 1 + marginHeight;
+ // +1 is for the line at the bottom of the tabs
+ }
+
+ // resize the scrollbar and close butotns
+ layoutButtons();
+}
+/**
+ * Paint the receiver.
+ */
+private void onPaint(Event event) {
+ GC gc = event.gc;
+ Rectangle rect = super.getClientArea();
+ if (items.length == 0) {
+
+ if (showBorders) {
+ gc.setForeground(borderColor1);
+ gc.drawRectangle(rect.x + BORDER_RIGHT,
+ rect.y + BORDER_BOTTOM,
+ rect.x + rect.width - BORDER_RIGHT - 1,
+ rect.y + rect.height - BORDER_BOTTOM - 1);
+ // fill in top and left edge with parent's background color
+ gc.setBackground(getParent().getBackground());
+ gc.fillRectangle(rect.x, rect.y, BORDER_RIGHT, rect.height);
+ gc.fillRectangle(rect.x, rect.y, rect.width, BORDER_BOTTOM);
+
+ }
+ if (fixedTabHeight > 0) {
+ int y = rect.y + BORDER_BOTTOM + fixedTabHeight;
+ if (onBottom) {
+ y = rect.y + rect.height - fixedTabHeight - 1;
+ }
+ gc.setForeground(borderColor1);
+ gc.drawLine(rect.x + BORDER_RIGHT, y, rect.x + rect.width, y);
+ }
+ gc.setForeground(getForeground());
+ gc.setBackground(getBackground());
+ return;
+ }
+
+ // redraw the Border
+ drawBorder(gc);
+
+ rect.x += BORDER_LEFT;
+ rect.y += BORDER_TOP;
+ rect.width -= BORDER_LEFT + BORDER_RIGHT;
+ rect.height -= BORDER_TOP + BORDER_BOTTOM;
+ gc.setClipping(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 - BORDER_LEFT - BORDER_RIGHT;
+ height = getTabHeight() + 1; // +1 causes top line between content and tabs to be redrawn
+ x = area.x + BORDER_LEFT;
+ y = area.y + BORDER_TOP;
+ if (onBottom) {
+ y = Math.max(0, area.y + area.height - BORDER_BOTTOM - 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) {
+ 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) {
+ 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;
+ 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;
+ }
+
+ Rectangle area = super.getClientArea();
+ if (oldArea == null || oldArea.width == 0 || oldArea.height == 0) {
+ layoutItems();
+ redraw();
+ } else {
+ if (onBottom && oldArea.height != area.height){
+ // move tabs up or down if tabs on bottom
+ layoutItems();
+ 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);
+
+ if (oldArea.width != area.width) {
+ // resize the widths so that all tabs are visible
+ layoutItems();
+ redrawTabArea(-1);
+ }
+ }
+ }
+ oldArea = area;
+
+ // 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);
+ color = getBackground();
+ Color foreground = getForeground();
+
+ // init inactive close button
+ inactiveCloseBar.setBackground(color);
+
+ // init scroll buttons
+ scrollBar.setBackground(color);
+
+ // init close button
+ if (gradientColors == null) {
+ closeBar.setBackground(color);
+ }
+}
+/**
+ * Specify a gradiant of colours to be draw in the background of the selected tab.
+ * For example to draw a gradiant 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 gradiant
+ * in order of appearance left to right. The value <code>null</code> clears the
+ * background gradiant. 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.
+ */
+
+public void setSelectionBackground(Color[] colors, int[] percents) {
+ 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;
+ closeBar.setBackground(background);
+ } 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];
+ if (getDisplay().getDepth() < 15) closeBar.setBackground(background);
+ else closeBar.setBackground(gradientColors[gradientColors.length - 1]);
+ }
+
+ // Refresh with the new settings
+ if (selectedIndex > -1) redrawTabArea(selectedIndex);
+}
+public void setSelectionBackground(Image image) {
+ if (image == backgroundImage) return;
+ gradientColors = null;
+ gradientPercents = null;
+ backgroundImage = image;
+ redrawTabArea(selectedIndex);
+}
+public void setBorderVisible(boolean show) {
+ if (showBorders == show) return;
+
+ showBorders = show;
+ if (showBorders) {
+ if ((getStyle() & SWT.FLAT) != 0) {
+ BORDER_BOTTOM = BORDER_TOP = BORDER_LEFT = BORDER_RIGHT = 1;
+ } else {
+ BORDER_LEFT = BORDER_TOP = 1;
+ BORDER_RIGHT = BORDER_BOTTOM = 3;
+ }
+ } else {
+ BORDER_BOTTOM = BORDER_TOP = BORDER_LEFT = BORDER_RIGHT = 0;
+ }
+ layoutItems();
+ redraw();
+}
+public void setFont(Font font) {
+ if (font != null && font.equals(getFont())) return;
+ super.setFont(font);
+ layoutItems();
+ redrawTabArea(-1);
+}
+public void setSelectionForeground (Color color) {
+ if (selectionForeground == color) return;
+ if (color == null) color = getForeground();
+ selectionForeground = color;
+ if (selectedIndex > -1) {
+ redrawTabArea(selectedIndex);
+ }
+}
+public void setInsertMark(CTabItem item, boolean after) {
+ int index = -1;
+ if (item != null) {
+ index = indexOf(item);
+ }
+ setInsertMark(index, after);
+}
+public void setInsertMark(int index, boolean after) {
+ 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.
+ */
+public void setSelection(int index) {
+ if (index < 0 || index >= items.length)
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+
+ if (selectedIndex == index) return;
+
+ if (showClose) {
+ inactiveCloseBar.setVisible(false);
+ inactiveItem = null;
+ }
+
+ 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);
+ }
+ }
+ ensureVisible();
+
+ redrawTabArea(-1);
+}
+private void ensureVisible() {
+ if (selectedIndex == -1) return;
+ // make sure selected item is visible
+ Rectangle area = super.getClientArea();
+ if (area.width == 0) return;
+ int areaWidth = area.x + area.width - BORDER_RIGHT;
+
+ CTabItem tabItem = items[selectedIndex];
+ if (selectedIndex < topTabIndex) {
+ topTabIndex = selectedIndex;
+ }
+ layoutItems();
+
+ int scrollWidth = scrollBar.getSize().x;
+ int width = areaWidth;
+ if (scroll_leftVisible() || scroll_rightVisible()) {
+ width -= scrollWidth;
+ }
+ while (tabItem.x + tabItem.width > width && selectedIndex != topTabIndex) {
+ topTabIndex++;
+ layoutItems();
+ width = areaWidth;
+ if (scroll_leftVisible() || scroll_rightVisible()) {
+ width -= scrollWidth;
+ }
+ }
+}
+/**
+ * Set the selection to the specified item.
+ */
+public void setSelection(CTabItem item) {
+ 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 setSelectionNotify(int index) {
+ int oldSelectedIndex = selectedIndex;
+ setSelection(index);
+ if (selectedIndex != oldSelectedIndex && selectedIndex != -1) {
+ Event event = new Event();
+ event.item = getItem(selectedIndex);
+ notifyListeners(SWT.Selection, event);
+ }
+}
+
+private void initCloseButtonImages() {
+ if (closeImage != null) return;
+
+ try {
+ Display display = getDisplay();
+ Image image = new Image(display, CTabFolder.class.getResourceAsStream("close.gif"));
+ ImageData source = image.getImageData();
+ ImageData mask = source.getTransparencyMask();
+ image.dispose();
+ closeImage = new Image(display, source, mask);
+ } catch (Error e) {
+ closeImage = null;
+ return;
+ }
+
+ closeItem.setDisabledImage(closeImage);
+ closeItem.setImage(closeImage);
+ inactiveCloseItem.setDisabledImage(closeImage);
+ inactiveCloseItem.setImage(closeImage);
+
+ int height = getTabHeight();
+ Point size = closeBar.computeSize(SWT.DEFAULT, height);
+ closeBar.setSize(size);
+ inactiveCloseBar.setSize(size);
+}
+private void initScrollBarImages() {
+ if (arrowLeftImage != null) return;
+
+ try {
+ Display display = getDisplay();
+ Image image = new Image(display, CTabFolder.class.getResourceAsStream("leftDisabled.gif"));
+ ImageData source = image.getImageData();
+ ImageData mask = source.getTransparencyMask();
+ image.dispose();
+ arrowLeftDisabledImage = new Image(display, source, mask);
+
+ image = new Image(display, CTabFolder.class.getResourceAsStream("left.gif"));
+ source = image.getImageData();
+ mask = source.getTransparencyMask();
+ image.dispose();
+ arrowLeftImage = new Image(display, source, mask);
+
+ image = new Image(display, CTabFolder.class.getResourceAsStream("rightDisabled.gif"));
+ source = image.getImageData();
+ mask = source.getTransparencyMask();
+ image.dispose();
+ arrowRightDisabledImage = new Image(display, source, mask);
+
+ image = new Image(display, CTabFolder.class.getResourceAsStream("right.gif"));
+ source = image.getImageData();
+ mask = source.getTransparencyMask();
+ image.dispose();
+ arrowRightImage = new Image(display, source, mask);
+ } catch (Error e) {
+ if (arrowLeftDisabledImage != null){
+ arrowLeftDisabledImage.dispose();
+ arrowLeftDisabledImage = null;
+ }
+ if (arrowLeftImage != null){
+ arrowLeftImage.dispose();
+ arrowLeftImage = null;
+ }
+ if (arrowRightDisabledImage != null){
+ arrowRightDisabledImage.dispose();
+ arrowRightDisabledImage = null;
+ }
+ if (arrowRightImage != null){
+ arrowRightImage.dispose();
+ arrowRightImage = null;
+ }
+ return;
+ }
+
+ scrollLeft.setDisabledImage(arrowLeftDisabledImage);
+ scrollLeft.setImage(arrowLeftImage);
+
+ scrollRight.setDisabledImage(arrowRightDisabledImage);
+ scrollRight.setImage(arrowRightImage);
+
+ int height = getTabHeight();
+ Point size = scrollBar.computeSize(SWT.DEFAULT, height);
+ scrollBar.setSize(size);
+}
+
+/**
+ * A mouse button was pressed down.
+ * If one of the tab scroll buttons was hit, scroll in the appropriate
+ * direction.
+ * If a tab was hit select the tab.
+ */
+private void onMouseUp(Event event) {
+ if (inDoubleClick) {
+ inDoubleClick = false;
+ Event e = new Event();
+ e.item = getItem(new Point(event.x, event.y));
+ notifyListeners(SWT.DefaultSelection, e);
+ return;
+ }
+
+ for (int i=0; i<items.length; i++) {
+ if (items[i].getBounds().contains(new Point(event.x, event.y))) {
+ setSelectionNotify(i);
+ return;
+ }
+ }
+}
+private void onMouseMove(Event event) {
+ if (!showClose) return;
+
+ CTabItem item = null;
+ Rectangle itemRect = null;
+ for (int i=0; i<items.length; i++) {
+ itemRect = items[i].getBounds();
+ if (itemRect.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;
+
+ Point closeRect = inactiveCloseBar.getSize();
+ int x = itemRect.x + itemRect.width - closeRect.x - 2;
+ int y = itemRect.y + 1 + (itemRect.height - 2 - closeRect.y)/2;
+ if (scrollBar.isVisible()) {
+ Rectangle scrollArea = scrollBar.getBounds();
+ scrollArea.width += BORDER_RIGHT;
+ if (scrollArea.contains(x, y)) return;
+ }
+
+ inactiveCloseBar.setLocation(x, y);
+ inactiveCloseBar.setVisible(true);
+ inactiveItem = item;
+}
+/**
+ * Answer the area where the left scroll button is drawn.
+ */
+private Rectangle scroll_getBounds() {
+ if (scrollBar != null)
+ return scrollBar.getBounds();
+ return new Rectangle(0, 0, 0, 0);
+}
+
+/**
+ * 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() {
+ if (topTabIndex < items.length - 1) {
+ // only show Scroll buttons if there is more than one item
+ // and if we are not alread at the last item
+ CTabItem tabItem = items[items.length-1];
+ int tabStopX = tabItem.x + tabItem.width;
+ Rectangle area = super.getClientArea();
+ if (tabStopX > area.x + area.width - BORDER_RIGHT) {
+ return true; // not all tabs fit in the client area
+ }
+ }
+ return false;
+}
+
+/**
+ * Scroll the tab items to the left.
+ */
+private void scroll_scrollLeft() {
+ if (scroll_leftVisible()) {
+ --topTabIndex;
+ layoutItems();
+ redrawTabArea(-1);
+ }
+}
+
+/**
+ * Scroll the tab items to the right.
+ */
+private void scroll_scrollRight() {
+ if (scroll_rightVisible()) {
+ topTabIndex++;
+ layoutItems();
+ redrawTabArea(-1);
+ }
+}
+public void setTabHeight(int height) {
+ if (height < 0) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ fixedTabHeight = height + CTabItem.TOP_MARGIN + CTabItem.BOTTOM_MARGIN;
+ layoutItems();
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderAdapter.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderAdapter.java
new file mode 100644
index 0000000000..1b72804de7
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderAdapter.java
@@ -0,0 +1,10 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+public class CTabFolderAdapter implements CTabFolderListener {
+ public void itemClosed(CTabFolderEvent event){};
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderEvent.java
new file mode 100644
index 0000000000..c19ecfc43d
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderEvent.java
@@ -0,0 +1,18 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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_j2se/org/eclipse/swt/custom/CTabFolderListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderListener.java
new file mode 100644
index 0000000000..c46e30c8d5
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabFolderListener.java
@@ -0,0 +1,12 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import java.util.EventListener;
+
+public interface CTabFolderListener {
+ public void itemClosed(CTabFolderEvent event);
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabItem.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabItem.java
new file mode 100644
index 0000000000..5a20b6622b
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/CTabItem.java
@@ -0,0 +1,436 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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
+ private static final int LEFT_MARGIN = 3;
+ private static final int RIGHT_MARGIN = 3;
+ static final int TOP_MARGIN = 2;
+ static final int BOTTOM_MARGIN = 2;
+ private static final int INTERNAL_SPACING = 2;
+
+ private static final String ellipsis = "...";
+
+/**
+ * Construct a CTabItem with the specified parent and style.
+ */
+public CTabItem (CTabFolder parent, int style) {
+ this(parent, style, parent.getItemCount());
+}
+/**
+ * Construct a CTabItem with the specified parent and style, inserted at
+ * the specified index.
+ */
+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;
+ super.dispose();
+ parent.destroyItem(this);
+ parent = null;
+ control = null;
+ toolTipText = null;
+}
+/**
+ * Return the bounds of the CTabItem.
+ */
+public Rectangle getBounds () {
+ return new Rectangle(x, y, width, height);
+}
+/**
+* Gets the control.
+* <p>
+* @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 () {
+ return control;
+}
+/**
+ * Answer the display of the receiver's parent widget.
+ */
+public Display getDisplay() {
+ if (parent == null) SWT.error(SWT.ERROR_WIDGET_DISPOSED);
+ return parent.getDisplay();
+}
+public Image getDisabledImage(){
+ return disabledImage;
+}
+/**
+ * Return the parent of the CTabItem.
+ */
+public CTabFolder getParent () {
+ return parent;
+}
+/**
+ * Gets the tool tip text.
+ * <p>
+ * @return the tool tip 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 getToolTipText () {
+ return toolTipText;
+}
+private int imageHeight() {
+ int imageHeight = 0;
+ if (parent.getImageHeight() != -1) {
+ imageHeight = parent.getImageHeight();
+ } else {
+ Image image = getImage();
+ if (image != null) {
+ imageHeight = image.getBounds().height;
+ }
+ }
+ return imageHeight;
+}
+private int imageWidth() {
+ int imageWidth = 0;
+ Image image = getImage();
+ if (image != null) {
+ imageWidth = image.getBounds().width;
+ }
+ return imageWidth;
+}
+/**
+ * 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) {
+ final Rectangle bounds;
+ if (index == parent.topTabIndex) {
+ bounds = new Rectangle(x + 1, y, width - 2, height);
+ } else {
+ bounds = new Rectangle(x + 2, y, width - 3, height);
+ }
+ 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
+ final 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 {
+ final 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);
+ final 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);
+ }
+ }
+ } 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 imageHeight = imageHeight();
+ int imageY = y + (height - parent.getImageHeight()) / 2;
+ gc.drawImage(image,
+ imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height,
+ xDraw, imageY, imageBounds.width, imageHeight);
+ xDraw += imageBounds.width + 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;
+ }
+ String text = shortenText(gc, getText(), textWidth);
+
+ if (isSelected && parent.selectionForeground != null) {
+ gc.setForeground(parent.selectionForeground);
+ } else {
+ gc.setForeground(parent.getForeground());
+ }
+ int textY = y + (height - textHeight(gc)) / 2;
+ gc.drawString(text, xDraw, textY, true);
+
+ gc.setForeground(parent.getForeground());
+}
+private String shortenText(GC gc, String text, int width) {
+ if (gc.textExtent(text).x <= width) return text;
+
+ int ellipseWidth = gc.textExtent(ellipsis).x;
+ int length = text.length();
+ int end = length - 1;
+ while (end > 0) {
+ text = text.substring(0, end);
+ int l1 = gc.textExtent(text).x;
+ if (l1 + ellipseWidth <= width) {
+ return text + ellipsis;
+ }
+ end--;
+ }
+ return "";
+}
+/**
+ * Answer the preferred height of the receiver for the GC.
+ */
+int preferredHeight(GC gc) {
+ return Math.max(textHeight(gc), imageHeight()) + TOP_MARGIN + BOTTOM_MARGIN;
+}
+/**
+ * Answer the preferred width of the receiver for the GC.
+ */
+int preferredWidth(GC gc) {
+ int tabWidth = LEFT_MARGIN + RIGHT_MARGIN;
+ Image image = getImage();
+ if (image != null) tabWidth += imageWidth() + INTERNAL_SPACING;
+ tabWidth += textWidth(gc);
+ if (parent.showClose) tabWidth += parent.closeBar.getSize().x;
+ return tabWidth;
+}
+/**
+ * Sets the control.
+ * <p>
+ * @param control the new 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 void setControl (Control control) {
+ if (control != null && 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);
+ }
+ }
+}
+/**
+ * Sets the image.
+ * <p>
+ * @param image the new image (or null)
+ *
+ * @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 setImage (Image image) {
+ Image oldImage = getImage();
+
+ super.setImage(image);
+ if (image == null || !image.equals(oldImage)) {
+ parent.itemChanged(this);
+ }
+}
+public void setDisabledImage (Image image) {
+ Image oldImage = getDisabledImage();
+
+ disabledImage = image;
+ if (disabledImage == null || !disabledImage.equals(oldImage)) {
+ parent.itemChanged(this);
+ }
+}
+
+/**
+ * Set the widget text.
+ * <p>
+ * This method sets the widget label. The label may include
+ * the mnemonic characters but must not contain line delimiters.
+ *
+ * @param string the new label for the widget
+ *
+ */
+public void setText (String string) {
+ String oldText = getText();
+
+ super.setText(string);
+ if (!string.equals(oldText)) {
+ parent.itemChanged(this);
+ }
+}
+
+/**
+ * Sets the tool tip text.
+ * <p>
+ * @param string the new tool tip text (or null)
+ *
+ * @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 setToolTipText (String string) {
+ toolTipText = string;
+}
+/**
+ * Answer the text height.
+ */
+private int textHeight(GC gc) {
+ int textHeight = 0;
+
+ if (isDisposed()) return textHeight;
+
+ String text = getText();
+ if (text != null) {
+ textHeight = gc.stringExtent(text).y;
+ }
+ return textHeight;
+}
+/**
+ * Answer the text width.
+ */
+private int textWidth(GC gc) {
+ int textWidth = 0;
+
+ if (isDisposed()) return 0;
+
+ String text = getText();
+ if (text != null) {
+ textWidth = gc.stringExtent(text).x;
+ }
+ return textWidth;
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ControlEditor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ControlEditor.java
new file mode 100644
index 0000000000..9dfd02a16d
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ControlEditor.java
@@ -0,0 +1,237 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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 internalListener;
+/**
+* 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;
+
+ internalListener = new Listener() {
+ public void handleEvent(Event e) {
+ if (e.widget instanceof ScrollBar && e.type == SWT.Selection)
+ scroll (e);
+ else if (e.type == SWT.Resize)
+ resize ();
+ }
+ };
+
+ parent.addListener (SWT.Resize, internalListener);
+
+ ScrollBar hBar = parent.getHorizontalBar ();
+ if (hBar != null) hBar.addListener (SWT.Selection, internalListener);
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.addListener (SWT.Selection, internalListener);
+}
+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, internalListener);
+ ScrollBar hBar = parent.getHorizontalBar ();
+ if (hBar != null) hBar.removeListener (SWT.Selection, internalListener);
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.removeListener (SWT.Selection, internalListener);
+ }
+
+ parent = null;
+ editor = null;
+ hadFocus = false;
+ internalListener = 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;
+}
+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;
+ if (editor.getVisible ()) {
+ hadFocus = editor.isFocusControl();
+ }
+ boolean visible = e.detail != SWT.DRAG;
+ if (visible) {
+ editor.setBounds (computeBounds ());
+ }
+ editor.setVisible (visible);
+ if (visible && hadFocus) editor.setFocus ();
+}
+/**
+* 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;
+
+ editor.setVisible (false);
+ editor.setBounds (computeBounds ());
+ editor.setVisible (true);
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultContent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultContent.java
new file mode 100644
index 0000000000..1ed76069b5
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultContent.java
@@ -0,0 +1,878 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import java.io.*;
+import java.util.*;
+
+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+(int)Math.pow(2, 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+(int)Math.pow(2, 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 buf.substring(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_j2se/org/eclipse/swt/custom/DefaultLineStyler.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultLineStyler.java
new file mode 100644
index 0000000000..1c82bbcd4c
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/DefaultLineStyler.java
@@ -0,0 +1,625 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import java.util.*;
+
+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) {
+ int size = styles.length;
+ if (styleCount == size) {
+ // expand the styles array by powers of 2
+ StyleRange[] newStyles = new StyleRange[size+(int)Math.pow(2, styleExpandExp)];
+ System.arraycopy(styles, 0, newStyles, 0, size);
+ styles = newStyles;
+ styleExpandExp++;
+ }
+ // shift the styles down to make room for the new style
+ for (int i=styleCount-1; i>=index; i--) {
+ styles[i+1]=styles[i];
+ }
+ styles[index] = style;
+ styleCount++;
+}
+/**
+ * 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;
+ 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
+ deleteStyle(i);
+ i--;
+ } else {
+ // beginning of overlap needs to be cleared
+ overlap.start=clearStyleEnd + 1;
+ overlap.length=overlapEnd - overlap.start + 1;
+ break;
+ }
+ }
+ count++;
+ }
+}
+/**
+ * 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((int)Math.pow(2, 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) {
+ // move the styles up
+ for (int i=index+1; i<styleCount; i++) {
+ styles[i-1] = styles[i];
+ }
+ styles[styleCount-1]=null;
+ styleCount--;
+}
+/**
+ * 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);
+ // 1GDX9PN
+ if (lineIndex > (lineBackgrounds.length - 1)) event.lineBackground = null;
+ else 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.add(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++;
+ }
+}
+/**
+ * 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;
+ 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
+ deleteStyle(index);
+ index--;
+ } else {
+ // style starts in change range, ends after change range
+ style.start = start;
+ style.length = styleEnd - end;
+ index++;
+ break;
+ }
+ }
+ }
+ }
+ // change the offsets of the styles after the affected styles
+ for (int i = index ; 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_j2se/org/eclipse/swt/custom/ExtendedModifyEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ExtendedModifyEvent.java
new file mode 100644
index 0000000000..517471267d
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ExtendedModifyEvent.java
@@ -0,0 +1,23 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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_j2se/org/eclipse/swt/custom/ExtendedModifyListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ExtendedModifyListener.java
new file mode 100644
index 0000000000..59952fcdda
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ExtendedModifyListener.java
@@ -0,0 +1,21 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import java.util.EventListener;
+
+public interface ExtendedModifyListener extends EventListener {
+/**
+ * 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_j2se/org/eclipse/swt/custom/LineBackgroundEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineBackgroundEvent.java
new file mode 100644
index 0000000000..711c84302d
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineBackgroundEvent.java
@@ -0,0 +1,25 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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_j2se/org/eclipse/swt/custom/LineBackgroundListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineBackgroundListener.java
new file mode 100644
index 0000000000..9607851c96
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineBackgroundListener.java
@@ -0,0 +1,23 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+
+import java.util.*;
+
+
+public interface LineBackgroundListener extends EventListener {
+
+/**
+ * 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_j2se/org/eclipse/swt/custom/LineStyleEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineStyleEvent.java
new file mode 100644
index 0000000000..86a7e0c7a6
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineStyleEvent.java
@@ -0,0 +1,24 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * 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_j2se/org/eclipse/swt/custom/LineStyleListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineStyleListener.java
new file mode 100644
index 0000000000..23fc3b518e
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/LineStyleListener.java
@@ -0,0 +1,20 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import java.util.*;
+
+public interface LineStyleListener extends EventListener {
+/**
+ * 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_j2se/org/eclipse/swt/custom/PopupList.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/PopupList.java
new file mode 100644
index 0000000000..5f650a3396
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/PopupList.java
@@ -0,0 +1,248 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+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_j2se/org/eclipse/swt/custom/ST.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ST.java
new file mode 100644
index 0000000000..5be042724e
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ST.java
@@ -0,0 +1,56 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+
+/**
+ * This class provides access to the public constants provided by <code>StyledText</code>.
+ */
+public class ST {
+
+ /* StyledText key action constants */
+ /* Navigation */
+ public static final int LINE_UP = SWT.ARROW_UP;
+ public static final int LINE_DOWN = SWT.ARROW_DOWN;
+ public static final int LINE_START = SWT.HOME;
+ public static final int LINE_END = SWT.END;
+ public static final int COLUMN_PREVIOUS = SWT.ARROW_LEFT;
+ public static final int COLUMN_NEXT = SWT.ARROW_RIGHT;
+ public static final int PAGE_UP = SWT.PAGE_UP;
+ public static final int PAGE_DOWN = SWT.PAGE_DOWN;
+ public static final int WORD_PREVIOUS = SWT.ARROW_LEFT | SWT.CTRL;
+ public static final int WORD_NEXT = SWT.ARROW_RIGHT | SWT.CTRL;
+ public static final int TEXT_START = SWT.HOME | SWT.CTRL;
+ public static final int TEXT_END = SWT.END | SWT.CTRL;
+ public static final int WINDOW_START = SWT.PAGE_UP | SWT.CTRL;
+ public static final int WINDOW_END = SWT.PAGE_DOWN | SWT.CTRL;
+
+ /* Selection */
+ public static final int SELECT_LINE_UP = SWT.ARROW_UP | SWT.SHIFT;
+ public static final int SELECT_LINE_DOWN = SWT.ARROW_DOWN | SWT.SHIFT;
+ public static final int SELECT_LINE_START = SWT.HOME | SWT.SHIFT;
+ public static final int SELECT_LINE_END = SWT.END | SWT.SHIFT;
+ public static final int SELECT_COLUMN_PREVIOUS = SWT.ARROW_LEFT | SWT.SHIFT;
+ public static final int SELECT_COLUMN_NEXT = SWT.ARROW_RIGHT | SWT.SHIFT;
+ public static final int SELECT_PAGE_UP = SWT.PAGE_UP | SWT.SHIFT;
+ public static final int SELECT_PAGE_DOWN = SWT.PAGE_DOWN | SWT.SHIFT;
+ public static final int SELECT_WORD_PREVIOUS = SWT.ARROW_LEFT | SWT.CTRL | SWT.SHIFT;
+ public static final int SELECT_WORD_NEXT = SWT.ARROW_RIGHT | SWT.CTRL | SWT.SHIFT;
+ public static final int SELECT_TEXT_START = SWT.HOME | SWT.CTRL | SWT.SHIFT;
+ public static final int SELECT_TEXT_END = SWT.END | SWT.CTRL | SWT.SHIFT;
+ public static final int SELECT_WINDOW_START = SWT.PAGE_UP | SWT.CTRL| SWT.SHIFT;
+ public static final int SELECT_WINDOW_END = SWT.PAGE_DOWN | SWT.CTRL | SWT.SHIFT;
+
+ /* Modification */
+ public static final int CUT = SWT.DEL | SWT.SHIFT;
+ public static final int COPY = SWT.INSERT | SWT.CTRL;
+ public static final int PASTE = SWT.INSERT | SWT.SHIFT;
+ public static final int DELETE_PREVIOUS = SWT.BS;
+ public static final int DELETE_NEXT = SWT.DEL;
+
+ /* Miscellaneous */
+ public static final int TOGGLE_OVERWRITE = SWT.INSERT;
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/SashForm.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/SashForm.java
new file mode 100644
index 0000000000..86abf0b90b
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/SashForm.java
@@ -0,0 +1,397 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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;
+
+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) {
+
+ Control[] controls = getControls(true);
+ if (controls.length == 0) return new Point(wHint, hHint);
+
+ int width = 0;
+ int height = 0;
+ boolean vertical = (getStyle() & SWT.VERTICAL) != 0;
+ if (vertical) {
+ width = wHint;
+ height += (controls.length - 1) * SASH_WIDTH;
+ } else {
+ height = hHint;
+ width += controls.length *SASH_WIDTH;
+ }
+ for (int i = 0; i < controls.length; i++) {
+ if (vertical) {
+ Point size = controls[i].computeSize(wHint, SWT.DEFAULT);
+ height += size.y;
+ } else {
+ Point size = controls[i].computeSize(SWT.DEFAULT, hHint);
+ width += size.x;
+ }
+ }
+ if (wHint != SWT.DEFAULT) width = wHint;
+ if (hHint != SWT.DEFAULT) height = hHint;
+
+ return new Point(width, height);
+}
+/**
+ * Answer SWT.HORIZONTAL if the controls in the SashForm are laid out side by side.
+ * Answer SWT.VERTICAL if the controls in the SashForm are laid out top to bottom.
+ */
+public int getOrientation() {
+ return orientation;
+}
+/**
+ * Answer the control that currently is maximized in the SashForm. This value may be null.
+ */
+public Control getMaximizedControl(){
+ 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).
+ */
+
+public int[] getWeights() {
+ 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) {
+ 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.
+ */
+public void setOrientation(int orientation) {
+ 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();
+}
+/**
+ * Sets the layout which is associated with the receiver to be
+ * the argument which may be null.
+ * <p>
+ * Note : SashForm 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) {
+}
+/**
+ * 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.
+ */
+public void setMaximizedControl(Control control){
+ 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();
+
+// // walk up
+// w= getParent();
+// if (w instanceof SplitForm)
+// ((SplitForm) w).internalMaximize(this);
+// else
+// layout(true);
+}
+
+/**
+ * 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.
+ */
+public void setWeights(int[] weights) {
+ 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_j2se/org/eclipse/swt/custom/ScrolledComposite.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ScrolledComposite.java
new file mode 100644
index 0000000000..fcc237a78c
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/ScrolledComposite.java
@@ -0,0 +1,378 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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;
+
+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;
+}
+
+/**
+ * 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() {
+ return alwaysShowScroll;
+}
+
+/**
+ * Get the content that is being scrolled.
+ */
+public Control getContent() {
+ 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) {
+ 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;
+}
+
+/**
+ * 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.
+ */
+public void setAlwaysShowScrollBars(boolean show) {
+ 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.
+ */
+public void setContent(Control content) {
+ 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.
+ */
+public void setExpandHorizontal(boolean expand) {
+ 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.
+ */
+public void setExpandVertical(boolean expand) {
+ 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
+ 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.
+ */
+public void setMinHeight(int height) {
+ setMinSize(minWidth, height);
+}
+public void setMinSize(Point size) {
+ setMinSize(size.x, size.y);
+}
+public void setMinSize(int width, int height) {
+ 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.
+ */
+
+public void setMinWidth(int width) {
+ setMinSize(width, minHeight);
+}
+
+private void vScroll() {
+ Control[] children = getChildren();
+ if (children.length == 0) return;
+ Control content = children[0];
+ 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_j2se/org/eclipse/swt/custom/StackLayout.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StackLayout.java
new file mode 100644
index 0000000000..02fab5cb46
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StackLayout.java
@@ -0,0 +1,74 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.layout.*;
+
+/**
+ * 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.
+ */
+
+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_j2se/org/eclipse/swt/custom/StyleRange.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyleRange.java
new file mode 100644
index 0000000000..10ca96699c
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyleRange.java
@@ -0,0 +1,146 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+public class StyleRange implements Cloneable {
+ 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() {
+ return start + length + foreground.hashCode() + background.hashCode() + 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_j2se/org/eclipse/swt/custom/StyledText.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledText.java
new file mode 100644
index 0000000000..d0f1d8257f
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledText.java
@@ -0,0 +1,6418 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.printing.*;
+import java.util.*;
+
+/**
+ * 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>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, SINGLE, READ_ONLY
+ * <dt><b>Events:</b><dd>ExtendedModify, LineGetBackground, LineGetStyle, Modify, Selection, Verify, VerifyKey
+ * </dl>
+ */
+public class StyledText extends Canvas {
+ static final char TAB = '\t';
+ private final static String PlatformLineDelimiter = System.getProperty("line.separator");
+
+ StyledTextContent content;
+ TextChangeListener textChangeListener; // listener for TextChanging, TextChanged and TextSet events from StyledTextContent
+ DefaultLineStyler defaultLineStyler;// used for setStyles API when no LineStyleListener is registered
+ 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 clientAreaHeight = 0; // the client area height. Needed to calculate content width for new
+ // visible lines during Resize callback
+ int contentWidth = 0; // width of widest known (already visible) line
+ int lineHeight; // line height=font height
+ int tabLength = 4; // number of characters in a tab
+ int tabWidth; // width of a tab character in the current GC
+ int lineEndSpaceWidth; // space, in pixel, used to indicated a selected line break
+ Cursor ibeamCursor;
+ 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
+ boolean editable = true;
+ 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();
+ Font boldFont;
+ Font regularFont;
+ 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 bidiColoring = false; // apply the BIDI algorithm on text segments of the same color
+ static final int BIDI_CARET_WIDTH = 4;
+ static int xInset = 0;
+ Image leftCaretBitmap = null;
+ Image rightCaretBitmap = null;
+ int caretDirection = SWT.NULL;
+ PaletteData caretPalette = null;
+ int lastCaretDirection = SWT.NULL;
+
+ 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 TextChanged = 3006;
+ static final int TextSet = 3004;
+ static final int VerifyKey = 3005;
+ /**
+ * 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();
+
+ /**
+ * 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());
+ }
+ /**
+ * 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;
+ }
+ /**
+ * 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\\deff0{\\fonttbl{\\f0\\fnil");
+ // specify code page, necessary for copy to work in bidi
+ // systems
+ String cpg = System.getProperty("file.encoding");
+ if (cpg.startsWith("Cp") || cpg.startsWith("MS")) {
+ cpg = cpg.substring(2, cpg.length());
+ header.append("\\cpg");
+ header.append(cpg);
+ }
+ header.append(" ");
+ 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 = getLineStyleData(lineOffset, line);
+ if (event != null) {
+ styles = event.styles;
+ }
+ event = 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 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) {
+ int index;
+
+ for (index = start; index < end; index++) {
+ char c = string.charAt(index);
+ if (c == '}' || c == '{' || c == '\\') {
+ break;
+ }
+ }
+ if (index == end) {
+ write(string.substring(start, end)); // string doesn't contain RTF formatting characters, write as is
+ }
+ else { // string needs to be transformed
+ char[] text = new char[end - start];
+
+ string.getChars(start, end, text, 0);
+ for (index = 0; index < text.length; index++) {
+ switch (text[index]) {
+ case '}':
+ case '{':
+ case '\\':
+ write("\\");
+ default:
+ write(text[index]);
+ }
+ }
+ }
+ }
+ /**
+ * 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 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;
+ }
+ // break on partial last line
+ if (style.start > endOffset) {
+ break;
+ }
+ // write any unstyled text
+ if (lineIndex < start) {
+ // copy to start of style or end of write range (specified
+ // during object creation) or end of line
+ copyEnd = Math.min(start, endOffset - lineOffset);
+ copyEnd = Math.min(copyEnd, lineLength);
+ write(line, lineIndex, copyEnd);
+ lineIndex = copyEnd;
+ if (copyEnd != start) {
+ break;
+ }
+ }
+ // 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 (specified
+ // during object creation) or end of line
+ copyEnd = Math.min(end, endOffset - lineOffset);
+ copyEnd = Math.min(copyEnd, lineLength);
+ write(line, lineIndex, copyEnd);
+ if (style.fontStyle == SWT.BOLD) {
+ write("\\b0");
+ }
+ write("}");
+ lineIndex = copyEnd;
+ if (copyEnd != end) {
+ break;
+ }
+ }
+ copyEnd = Math.min(lineLength, endOffset - lineOffset);
+ if (lineIndex < copyEnd) {
+ write(line, lineIndex, copyEnd);
+ }
+ 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);
+ }
+ }
+
+public StyledText(Composite parent, int style) {
+ // use NO_BACKGROUND style when implemented by SWT.
+ // always need to draw background in drawLine when using NO_BACKGROUND!
+ super(parent, checkStyle(style | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND));
+ Display display = getDisplay();
+
+ if ((style & SWT.READ_ONLY) != 0) {
+ setEditable(false);
+ }
+ clipboard = new Clipboard(display);
+ calculateLineHeight();
+ calculateTabWidth();
+ installDefaultContent();
+ setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
+ setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ initializeFonts();
+ if (isBidi() == false) {
+ Caret caret = new Caret(this, SWT.NULL);
+ caret.setSize(1, caret.getSize().y);
+ }
+ else {
+ createCaretBitmaps();
+ createBidiCaret();
+ xInset = BIDI_CARET_WIDTH - 1;
+ Runnable runnable = new Runnable() {
+ public void run() {
+ createBidiCaret();
+ Caret caret = getCaret();
+ // don't use setBidiCaret, will position the caret
+ // incorrectly
+ int caretX = caret.getLocation().x;
+ if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
+ caretX -= (getCaretWidth() - 1);
+ } else {
+ caretX += (getCaretWidth() - 1);
+ }
+ int line = content.getLineAtOffset(caretOffset);
+ caret.setLocation(caretX, line * lineHeight - verticalScrollOffset);
+ }
+ };
+ StyledTextBidi.addLanguageListener(this.handle, runnable);
+ }
+ // 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();
+}
+
+/**
+ * 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>
+ * IMPORTANT: This API is in-progress and may change in the future.
+ * </p>
+ */
+public void setBidiColoring(boolean mode) {
+ checkWidget();
+ bidiColoring = mode;
+}
+
+/**
+ * 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>
+ * IMPORTANT: This API is in-progress and may change in the future.
+ * </p>
+ */
+public boolean getBidiColoring() {
+ checkWidget();
+ return bidiColoring;
+}
+
+int [] getStyleOffsets (String line, int lineOffset) {
+ StyledTextEvent event = getLineStyleData(lineOffset, line);
+ StyleRange [] styles = new StyleRange [0];
+ if (event != null) {
+ styles = event.styles;
+ }
+ if (styles.length == 0 || !bidiColoring) {
+ return new int[] {0, line.length()};
+ }
+
+ int k=0, count = 1;
+ while (k < styles.length && styles[k].start == 0 && styles[k].length == line.length()) {
+ 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 (line.length() > offsets[count-1]) {
+ offsets [count] = line.length();
+ count++;
+ }
+ if (count == offsets.length)
+ return offsets;
+ int [] result = new int [count];
+ System.arraycopy (offsets, 0, result, 0, count);
+ return result;
+}
+
+/**
+ * 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 (one or more of SWT.CTRL, SWT.SHIFT, SWT.ALT)
+ * @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();
+ if (action == SWT.NULL) {
+ keyActionMap.remove(new Integer(key));
+ }
+ else {
+ keyActionMap.put(new Integer(key), new Integer(action));
+ }
+}
+/**
+ * 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, content.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) {
+ int itemCount = (int) Math.ceil((float) getClientArea().height / lineHeight);
+ calculateContentWidth(topIndex, Math.min(itemCount, content.getLineCount() - topIndex));
+ }
+}
+/**
+ * Calculates the width of the widget text in the specified line range.
+ * <p>
+ *
+ * @param startline the first line
+ * @param lineCount number of lines to consider for the calculation
+ */
+void calculateContentWidth(int startLine, int lineCount) {
+ String line;
+ GC gc = new GC(this);
+ FontData fontData = gc.getFont().getFontData()[0];
+ int stopLine;
+ boolean isBidi = isBidi();
+
+ if (lineCount < 0) {
+ startLine += lineCount;
+ lineCount *= -1;
+ }
+ stopLine = startLine + lineCount;
+ setLineFont(gc, fontData, SWT.BOLD);
+ for (int i = startLine; i < stopLine; i++) {
+ line = content.getLine(i);
+ if (isBidi) {
+ int lineOffset = content.getOffsetAtLine (i);
+ StyledTextBidi bidi = new StyledTextBidi(gc, tabWidth, line, null, null, getStyleOffsets (line, lineOffset));
+ contentWidth = Math.max(bidi.getTextWidth() + getCaretWidth(), contentWidth);
+ }
+ else {
+ contentWidth = Math.max(contentWidth(line, i, gc) + getCaretWidth(), contentWidth);
+ }
+ }
+ gc.dispose();
+}
+/**
+ * Calculates the line height
+ */
+void calculateLineHeight() {
+ GC gc = new GC(this);
+ lineHeight = gc.getFontMetrics().getHeight();
+ gc.dispose();
+}
+/**
+ * Calculates the width in pixel of a tab character
+ */
+void calculateTabWidth() {
+ StringBuffer tabBuffer = new StringBuffer(tabLength);
+ GC gc = new GC(this);
+
+ for (int i = 0; i < tabLength; i++) {
+ tabBuffer.append(' ');
+ }
+ tabWidth = gc.stringExtent(tabBuffer.toString()).x;
+ gc.dispose();
+}
+/**
+ * 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());
+ }
+}
+/**
+ * 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, contentWidth - getClientArea().width);
+
+ 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);
+ }
+}
+/**
+ * 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;
+ count = content.getLineCount();
+
+ // If a height or width has been specified (via hHint and wHint),
+ // use those values. Otherwise calculate the size based on the
+ // text that is defined.
+ if (hHint != SWT.DEFAULT) {
+ height = hHint;
+ } else {
+ if (singleLine) count = 1;
+ height = count * lineHeight;
+ }
+ if (wHint != SWT.DEFAULT) {
+ width = wHint;
+ } else {
+ // 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().width / lineHeight);
+ calculateContentWidth(0, visibleCount);
+ width = contentWidth;
+ }
+
+ // 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;
+ }
+
+ // Hardcode the inset margins. Assume text is inset
+ // 1 pixel on each side.
+ width += 2;
+ height += 2;
+
+ Rectangle rect = computeTrim(0,0,width,height);
+ return new Point (rect.width, rect.height);
+}
+/**
+ * Returns the width of the specified text. Expand tabs to tab stops using
+ * the widget tab width.
+ * This is a quick and inaccurate measurement. Text styles are not taken
+ * into consideration. The gc should be setup to reflect the widest
+ * possible font style.
+ * <p>
+ *
+ * @param text text to be measured.
+ * @param lineIndex index of the line.
+ * @param gc GC to use for measuring text
+ * @return width of the text with tabs expanded to tab stops
+ */
+int contentWidth(String text, int lineIndex, GC gc) {
+ int paintX = 0;
+ int textLength = text.length();
+
+ for (int i = 0; i < textLength; i++) {
+ int tabIndex = text.indexOf(TAB, i);
+ // is tab not present or past the rendering range?
+ if (tabIndex == -1 || tabIndex > textLength) {
+ tabIndex = textLength;
+ }
+ if (tabIndex != i) {
+ String tabSegment = text.substring(i, tabIndex);
+ paintX += gc.stringExtent(tabSegment).x;
+ if (tabIndex != textLength && tabWidth > 0) {
+ paintX += tabWidth;
+ paintX -= paintX % tabWidth;
+ }
+ i = tabIndex;
+ }
+ else
+ if (tabWidth > 0) {
+ paintX += tabWidth;
+ paintX -= paintX % tabWidth;
+ }
+ }
+ return paintX;
+}
+/**
+ * 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) {
+ RTFTransfer rtfTransfer = RTFTransfer.getInstance();
+ TextTransfer plainTextTransfer = TextTransfer.getInstance();
+ RTFWriter rtfWriter = new RTFWriter(selection.x, length);
+ TextWriter plainTextWriter = new TextWriter(selection.x, length);
+ String rtfText = getPlatformDelimitedText(rtfWriter);
+ String plainText = getPlatformDelimitedText(plainTextWriter);
+
+ try {
+ clipboard.setContents(
+ new String[]{rtfText, plainText},
+ new Transfer[]{rtfTransfer, plainTextTransfer});
+ }
+ catch (SWTError error) {
+ // Copy to clipboard failed. This happens when another application
+ // is accessing the clipboard while we copy. Ignore the error.
+ // Fixes 1GDQAVN
+ }
+ }
+}
+/**
+ * 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.CTRL, ST.WORD_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT | SWT.CTRL, ST.WORD_NEXT);
+ setKeyBinding(SWT.HOME | SWT.CTRL, ST.TEXT_START);
+ setKeyBinding(SWT.END | SWT.CTRL, ST.TEXT_END);
+ setKeyBinding(SWT.PAGE_UP | SWT.CTRL, ST.WINDOW_START);
+ setKeyBinding(SWT.PAGE_DOWN | SWT.CTRL, ST.WINDOW_END);
+
+ // Selection
+ setKeyBinding(SWT.ARROW_UP | SWT.SHIFT, ST.SELECT_LINE_UP);
+ setKeyBinding(SWT.ARROW_DOWN | SWT.SHIFT, ST.SELECT_LINE_DOWN);
+ setKeyBinding(SWT.HOME | SWT.SHIFT, ST.SELECT_LINE_START);
+ setKeyBinding(SWT.END | SWT.SHIFT, ST.SELECT_LINE_END);
+ setKeyBinding(SWT.ARROW_LEFT | SWT.SHIFT, ST.SELECT_COLUMN_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT | SWT.SHIFT, ST.SELECT_COLUMN_NEXT);
+ setKeyBinding(SWT.PAGE_UP | SWT.SHIFT, ST.SELECT_PAGE_UP);
+ setKeyBinding(SWT.PAGE_DOWN | SWT.SHIFT, ST.SELECT_PAGE_DOWN);
+ setKeyBinding(SWT.ARROW_LEFT | SWT.CTRL | SWT.SHIFT, ST.SELECT_WORD_PREVIOUS);
+ setKeyBinding(SWT.ARROW_RIGHT | SWT.CTRL | SWT.SHIFT, ST.SELECT_WORD_NEXT);
+ setKeyBinding(SWT.HOME | SWT.CTRL | SWT.SHIFT, ST.SELECT_TEXT_START);
+ setKeyBinding(SWT.END | SWT.CTRL | SWT.SHIFT, ST.SELECT_TEXT_END);
+ setKeyBinding(SWT.PAGE_UP | SWT.CTRL | SWT.SHIFT, ST.SELECT_WINDOW_START);
+ setKeyBinding(SWT.PAGE_DOWN | SWT.CTRL | SWT.SHIFT, ST.SELECT_WINDOW_END);
+
+ // Modification
+ // Cut, Copy, Paste
+ // CUA style
+ setKeyBinding('\u0018' | SWT.CTRL, ST.CUT);
+ setKeyBinding('\u0003' | SWT.CTRL, ST.COPY);
+ setKeyBinding('\u0016' | SWT.CTRL, ST.PASTE);
+ // Wordstar style
+ setKeyBinding(SWT.DEL | SWT.SHIFT, ST.CUT);
+ setKeyBinding(SWT.INSERT | SWT.CTRL, ST.COPY);
+ setKeyBinding(SWT.INSERT | SWT.SHIFT, ST.PASTE);
+
+ setKeyBinding(SWT.BS, ST.DELETE_PREVIOUS);
+ setKeyBinding(SWT.DEL, ST.DELETE_NEXT);
+
+ // Miscellaneous
+ setKeyBinding(SWT.INSERT, ST.TOGGLE_OVERWRITE);
+}
+/**
+ * Create the bidi caret. Use the caret for the current keyboard
+ * mode.
+ */
+void createBidiCaret() {
+ Caret caret = getCaret();
+ if (caret == null) {
+ caret = new Caret(this, SWT.NULL);
+ }
+
+ int direction = StyledTextBidi.getKeyboardLanguageDirection();
+ if (direction == caretDirection) {
+ return;
+ }
+ caretDirection = direction;
+ if (caretDirection == SWT.LEFT) {
+ caret.setImage(leftCaretBitmap);
+ }
+ else
+ if (caretDirection == SWT.RIGHT) {
+ caret.setImage(rightCaretBitmap);
+ }
+}
+/**
+ * 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();
+ if (selection.y > selection.x) {
+ copy();
+ 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 < 0) doAutoScroll(SWT.LEFT);
+ else if (event.x > area.width) 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) {
+ doLineUp();
+ doSelection(SWT.LEFT);
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ } else if (direction == SWT.DOWN) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.DOWN) {
+ doLineDown();
+ doSelection(SWT.RIGHT);
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ } else if (direction == SWT.RIGHT) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.RIGHT) {
+ doColumnRight();
+ doSelection(SWT.RIGHT);
+ display.timerExec(TIMER_INTERVAL, this);
+ }
+ }
+ };
+ } else if (direction == SWT.LEFT) {
+ timer = new Runnable() {
+ public void run() {
+ if (autoScrollDirection == SWT.LEFT) {
+ doColumnLeft();
+ 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);
+ }
+}
+void doBidiCursorNext() {
+ if (selection.y - selection.x > 0) {
+ caretOffset = selection.y;
+ showCaret();
+ }
+ else {
+ doBidiSelectionCursorNext();
+ }
+}
+void doBidiCursorPrevious() {
+ if (selection.y - selection.x > 0) {
+ caretOffset = selection.x;
+ showCaret();
+ }
+ else {
+ doBidiSelectionCursorPrevious();
+ }
+}
+void doBidiMouseLocationChange(int x, int y, boolean select) {
+ int line = (y + verticalScrollOffset) / lineHeight;
+ int lineCount = content.getLineCount();
+
+ if (line > lineCount - 1) {
+ line = lineCount - 1;
+ }
+ if (line >= 0) {
+ String lineText = content.getLine(line);
+ int lineOffset = content.getOffsetAtLine(line);
+
+
+ GC gc = new GC(this);
+ StyleRange[] styles = null;
+ StyledTextEvent event = getLineStyleData(lineOffset, lineText);
+
+ x += horizontalScrollOffset;
+ if (event != null) {
+ styles = filterLineStyles(event.styles);
+ }
+ lastCaretDirection = SWT.NULL;
+ int[] boldStyles = getBoldRanges(styles, lineOffset, lineText.length());
+ StyledTextBidi bidi = new StyledTextBidi(gc, tabWidth, lineText, boldStyles, boldFont, getStyleOffsets (lineText, lineOffset));
+ int[] values = bidi.getCaretOffsetAndDirectionAtX(x);
+ int offsetInLine = values[0];
+ lastCaretDirection = values[1];
+ int newCaretOffset = lineOffset + offsetInLine;
+// if (newCaretOffset != caretOffset) {
+ caretOffset = newCaretOffset;
+ if (select) {
+ doMouseSelection();
+ }
+ Caret caret = getCaret();
+ if (caret != null) {
+ int caretX = bidi.getCaretPosition(offsetInLine, lastCaretDirection);
+ caretX = caretX - horizontalScrollOffset;
+ if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
+ caretX -= (getCaretWidth() - 1);
+ }
+ createBidiCaret();
+ caret.setLocation(caretX, line * lineHeight - verticalScrollOffset);
+ }
+// }
+ if (select == false) {
+ clearSelection(true);
+ }
+ }
+}
+void doBidiSelectionCursorNext() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (offsetInLine < content.getLine(line).length()) {
+ caretOffset++;
+ boolean scrolled = scrollBidiCaret();
+ if (scrolled) return;
+ // remember the last direction, should reset
+ // this variable whenever the caret offset changes
+ lastCaretDirection = ST.COLUMN_NEXT;
+ setBidiCaret(false);
+ }
+ else
+ if (line < content.getLineCount() - 1) {
+ line++;
+ caretOffset = content.getOffsetAtLine(line);
+ showCaret();
+ }
+}
+void doBidiSelectionCursorPrevious() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (offsetInLine > 0) {
+ caretOffset--;
+ boolean scrolled = scrollBidiCaret();
+ if (scrolled) return;
+ // remember the last direction, should reset
+ // this variable whenever the caret offset changes
+ lastCaretDirection = ST.COLUMN_PREVIOUS;
+ setBidiCaret(false);
+ }
+ else
+ if (line > 0) {
+ line--;
+ lineOffset = content.getOffsetAtLine(line);
+ caretOffset = lineOffset + content.getLine(line).length();
+ showCaret();
+ }
+}
+/**
+ * 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()) {
+ GC gc = new GC(this);
+ String lineText = content.getLine(line);
+ int lineLength = lineText.length();
+ StyledTextEvent event = getLineStyleData(lineOffset, lineText);
+ int[] boldStyles = null;
+ StyledTextBidi bidi;
+
+ if (event != null) {
+ boldStyles = getBoldRanges(event.styles, lineOffset, lineLength);
+ }
+ bidi = new StyledTextBidi(gc, tabWidth, lineText, boldStyles, boldFont, getStyleOffsets (lineText, lineOffset));
+ 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) {
+ // 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.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 > 1 && bidi.isRightToLeft(caretOffset - lineOffset - 2)) {
+ caretOffset--;
+ }
+ }
+ }
+ else
+ if (offsetInLine == lineLength && bidi.getCaretPosition(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 + lineLength && bidi.isRightToLeft(caretOffset - lineOffset)) {
+ caretOffset--;
+ }
+ }
+ // if new caret position is to the left of the client area
+ if (bidi.getCaretPosition(caretOffset - lineOffset) < horizontalScrollOffset) {
+ // scroll to the caret position
+ showCaret();
+ }
+ else {
+ // otherwise just update caret position without scrolling it into view
+ setCaretLocation();
+ }
+ }
+ 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 = new GC(this);
+ StyledTextEvent event = getLineStyleData(lineOffset, lineText);
+ int[] boldStyles = null;
+ StyledTextBidi bidi;
+
+ if (event != null) {
+ boldStyles = getBoldRanges(event.styles, lineOffset, lineLength);
+ }
+ bidi = new StyledTextBidi(gc, tabWidth, lineText, boldStyles, boldFont, getStyleOffsets (lineText, lineOffset));
+ if (bidi.getTextWidth() > 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() > 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.getCaretPosition(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.getCaretPosition(offsetInLine) >= horizontalScrollOffset) {
+ // scroll to the caret position
+ showCaret();
+ }
+ else {
+ // otherwise just update caret position without scrolling it into view
+ setCaretLocation();
+ }
+ 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();
+ // 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.getCaretPosition(offsetInLine + 1) < clientAreaEnd &&
+ bidi.getCaretPosition(lineLength) < 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() {
+ 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 #doSelectionCursorLeft
+ */
+void doCursorPrevious() {
+ if (isBidi()) {
+ doBidiCursorPrevious();
+ return;
+ }
+ if (selection.y - selection.x > 0) {
+ caretOffset = selection.x;
+ showCaret();
+ }
+ 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 #doSelectionCursorRight
+ */
+void doCursorNext() {
+ if (isBidi()) {
+ doBidiCursorNext();
+ return;
+ }
+ if (selection.y - selection.x > 0) {
+ caretOffset = selection.y;
+ showCaret();
+ }
+ 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);
+ }
+}
+/**
+ * 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.
+ * Make the new caret position visible.
+ */
+void doLineDown() {
+ doSelectionLineDown();
+ showCaret();
+}
+/**
+ * Moves the caret to the end of the line.
+ */
+void doLineEnd() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int lineLength = content.getLine(line).length();
+ int lineEndOffset = lineOffset + lineLength;
+
+ if (caretOffset < lineEndOffset) {
+ caretOffset = lineEndOffset;
+ showCaret();
+ }
+}
+/**
+ * Moves the caret to the beginning of the line.
+ */
+void doLineStart() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+
+ if (caretOffset > lineOffset) {
+ caretOffset = lineOffset;
+ showCaret();
+ }
+}
+/**
+ * Moves the caret one line up and to the same character offset relative
+ * to the beginning of the line. Move the caret to the end of the new line
+ * if the new line is shorter than the character offset.
+ */
+void doLineUp() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (line > 0) {
+ line--;
+ lineOffset = content.getOffsetAtLine(line);
+ int lineLength = content.getLine(line).length();
+ if (offsetInLine > lineLength) {
+ offsetInLine = lineLength;
+ }
+ caretOffset = lineOffset + offsetInLine;
+ showCaret();
+ }
+}
+/**
+ * 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) {
+ if (isBidi()) {
+ doBidiMouseLocationChange(x, y, select);
+ return;
+ }
+ int line = (y + verticalScrollOffset) / lineHeight;
+ int lineCount = content.getLineCount();
+
+ if (line > lineCount - 1) {
+ line = lineCount - 1;
+ }
+ if (line >= 0) {
+ String lineText = content.getLine(line);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = getCaretOffsetAtX(lineText, lineOffset, x);
+ int newCaretOffset = lineOffset + offsetInLine;
+
+ if (newCaretOffset != caretOffset) {
+ caretOffset = newCaretOffset;
+ if (select) {
+ doMouseSelection();
+ }
+ setCaretLocation();
+ }
+ 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);
+ }
+}
+/**
+ * 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 line = content.getLineAtOffset(caretOffset);
+ int lineCount = content.getLineCount();
+
+ if (line < lineCount) {
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+ int lineLength;
+ int verticalMaximum = content.getLineCount() * getVerticalIncrement();
+ int pageSize = getClientArea().height;
+ int scrollLines = Math.min(lineCount - line - 1, getLineCountWhole() - 1);
+ int scrollOffset;
+
+ line += scrollLines;
+ lineOffset = content.getOffsetAtLine(line);
+ lineLength = content.getLine(line).length();
+ // set cursor to end of line if cursor would be beyond the end
+ // of line or if page down goes to last line
+ if (offsetInLine > lineLength || line == lineCount - 1) {
+ offsetInLine = lineLength;
+ }
+ caretOffset = lineOffset + offsetInLine;
+ 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);
+ }
+ else {
+ showCaret();
+ }
+ }
+}
+/**
+ * Moves the cursor to the end of the last fully visible line.
+ */
+void doPageEnd() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineCount = content.getLineCount();
+
+ if (line < lineCount) {
+ line = getBottomIndex();
+ caretOffset = content.getOffsetAtLine(line) + content.getLine(line).length();
+ showCaret();
+ }
+}
+/**
+ * Moves the cursor to the beginning of the first fully visible line.
+ */
+void doPageStart() {
+ if (content.getLineAtOffset(caretOffset) > topIndex) {
+ caretOffset = content.getOffsetAtLine(topIndex);
+ showCaret();
+ }
+}
+/**
+ * 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 line = content.getLineAtOffset(caretOffset);
+
+ if (line > 0) {
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+ int lineLength;
+ int scrollLines = Math.min(line, getLineCountWhole() - 1);
+ int scrollOffset;
+
+ line -= scrollLines;
+ lineOffset = content.getOffsetAtLine(line);
+ lineLength = content.getLine(line).length();
+ // set cursor to start of line if page up goes to first line
+ if (line == 0) {
+ offsetInLine = 0;
+ }
+ else
+ if (offsetInLine > lineLength) {
+ offsetInLine = lineLength;
+ }
+ caretOffset = lineOffset + offsetInLine;
+ // scroll one page up or to the top
+ scrollOffset = Math.max(0, verticalScrollOffset - scrollLines * getVerticalIncrement());
+ if (scrollOffset < verticalScrollOffset) {
+ setVerticalScrollOffset(scrollOffset, true);
+ }
+ else {
+ showCaret();
+ }
+ }
+}
+/**
+ * Moves the caret one line down and to the same character offset relative
+ * to the beginning of the line. Move the caret to the end of the new line
+ * if the new line is shorter than the character offset.
+ */
+void doSelectionLineDown() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (line < content.getLineCount() - 1) {
+ line++;
+ lineOffset = content.getOffsetAtLine(line);
+ int lineLength = content.getLine(line).length();
+ if (offsetInLine > lineLength) {
+ offsetInLine = lineLength;
+ }
+ caretOffset = lineOffset + offsetInLine;
+ }
+}
+/**
+ * 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 previous character or to the end of the previous
+ * line if the cursor is at the beginning of a line.
+ */
+void doSelectionCursorPrevious() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (offsetInLine > 0) {
+ caretOffset--;
+ showCaret();
+ }
+ else
+ if (line > 0) {
+ line--;
+ lineOffset = content.getOffsetAtLine(line);
+ caretOffset = lineOffset + content.getLine(line).length();
+ showCaret();
+ }
+}
+/**
+ * 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 line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+
+ if (offsetInLine < content.getLine(line).length()) {
+ caretOffset++;
+ showCaret();
+ }
+ else
+ if (line < content.getLineCount() - 1) {
+ line++;
+ caretOffset = content.getOffsetAtLine(line);
+ showCaret();
+ }
+}
+/**
+ * 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) {
+ caretOffset = selection.x;
+ showCaret();
+ }
+ else {
+ doSelectionWordPrevious();
+ }
+}
+/**
+ * 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) {
+ caretOffset = selection.y;
+ showCaret();
+ }
+ else {
+ doSelectionWordNext();
+ }
+}
+/**
+ * Moves the caret to the start of the previous word.
+ */
+void doSelectionWordPrevious() {
+ caretOffset = getWordStart(caretOffset);
+ showCaret();
+}
+/**
+ * Moves the caret to the end of the next word .
+ */
+void doSelectionWordNext() {
+ caretOffset = getWordEnd(caretOffset);
+ showCaret();
+}
+/**
+ * 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, y, 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; // adjust y position for pixel based scrolling
+ int lineCount = content.getLineCount();
+ Color background = getBackground();
+ Color foreground = getForeground();
+ GC gc = new GC(this);
+
+ 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);
+ drawLine(line, i, paintY, gc, background, foreground, clearBackground);
+ }
+ gc.dispose();
+ }
+}
+/**
+ * Draws a line of text at the specified location.
+ * <p>
+ *
+ * @param line the line to draw
+ * @param lineIndex index of the line to draw
+ * @param paintY y location to draw at
+ * @param gc GC to draw on
+ * @param widgetBackground the widget background color. Used as the default rendering color.
+ * @param widgetForeground the widget foreground color. Used as the default rendering color.
+ */
+void drawLine(String line, int lineIndex, int paintY, GC gc, Color widgetBackground, Color widgetForeground, boolean clearBackground) {
+ int lineOffset = content.getOffsetAtLine(lineIndex);
+ int lineLength = line.length();
+ int selectionStart = selection.x;
+ int selectionEnd = selection.y;
+ StyleRange[] styles = new StyleRange[0];
+ Color lineBackground = null;
+ StyledTextEvent event = getLineStyleData(lineOffset, line);
+ StyledTextBidi bidi = null;
+
+ if (event != null) {
+ styles = event.styles;
+ }
+ if (isBidi()) {
+ int[] boldStyles = getBoldRanges(styles, lineOffset, lineLength);
+ setLineFont(gc, gc.getFont().getFontData()[0], SWT.NORMAL);
+ bidi = new StyledTextBidi(gc, tabWidth, line, boldStyles, boldFont, getStyleOffsets (line, lineOffset));
+ }
+ event = getLineBackgroundData(lineOffset, line);
+ if (event != null) {
+ lineBackground = event.lineBackground;
+ }
+ if (lineBackground == null) {
+ lineBackground = widgetBackground;
+ }
+ if (clearBackground && ((getStyle() & SWT.FULL_SELECTION) == 0 || selectionStart > lineOffset || selectionEnd <= lineOffset + lineLength)) {
+ // draw background if full selection is off or if line is not completely selected
+ gc.setBackground(lineBackground);
+ gc.setForeground(lineBackground);
+ gc.fillRectangle(0, paintY, getClientArea().width, lineHeight);
+ }
+ if (selectionStart != selectionEnd) {
+ drawLineSelectionBackground(line, lineOffset, styles, paintY, gc, bidi);
+ }
+ if (selectionStart != selectionEnd && ((selectionStart >= lineOffset && selectionStart < lineOffset + lineLength) || (selectionStart < lineOffset && selectionEnd > lineOffset))) {
+ styles = getSelectionLineStyles(styles);
+ }
+ if (isBidi()) {
+ int paintX = textWidth(line, lineOffset, 0, 0, styles, 0, gc, bidi);
+ drawStyledLine(line, lineOffset, 0, styles, paintX, paintY, gc, lineBackground, widgetForeground, bidi);
+ }
+ else {
+ drawStyledLine(line, lineOffset, 0, styles, 0, paintY, gc, lineBackground, widgetForeground, bidi);
+ }
+}
+/**
+ * Draws the background of the line selection.
+ * <p>
+ *
+ * @param line the line to draw
+ * @param lineOffset offset of the first character in the line.
+ * Relative to the start of the document.
+ * @param styles line styles
+ * @param paintY y location to draw at
+ * @param gc GC to draw on
+ * @param bidi the bidi object to use for measuring and rendering text in bidi locales.
+ * null when not in bidi mode.
+ */
+void drawLineSelectionBackground(String line, int lineOffset, StyleRange[] styles, int paintY, GC gc, StyledTextBidi bidi) {
+ int lineLength = line.length();
+ int paintX;
+ int selectionBackgroundWidth = -1;
+ int selectionStart = Math.max(0, selection.x - lineOffset);
+ int selectionEnd = selection.y - lineOffset;
+ int selectionLength = selectionEnd - selectionStart;
+
+ if (selectionEnd == selectionStart || selectionEnd < 0 || selectionStart > lineLength) {
+ return;
+ }
+ paintX = textWidth(line, lineOffset, 0, selectionStart, filterLineStyles(styles), 0, gc, bidi);
+ // selection extends past end of line?
+ if (selectionEnd > lineLength) {
+ if ((getStyle() & SWT.FULL_SELECTION) != 0) {
+ // use the greater of the client area width and the content width
+ // fixes 1G8IYRD
+ selectionBackgroundWidth = Math.max(getClientArea().width, contentWidth);
+ }
+ else {
+ selectionLength = lineLength - selectionStart;
+ }
+ }
+ gc.setBackground(getSelectionBackground());
+ gc.setForeground(getSelectionForeground());
+ if (selectionBackgroundWidth == -1) {
+ selectionBackgroundWidth = textWidth(line, lineOffset, selectionStart, selectionLength, styles, paintX, gc, bidi);
+ if (selectionBackgroundWidth < 0) {
+ // width can be negative when in R2L bidi segment
+ paintX += selectionBackgroundWidth;
+ selectionBackgroundWidth *= -1;
+ }
+ if (selectionEnd > lineLength) {
+ selectionEnd = selectionStart + selectionLength;
+ // if the selection extends past this line, render an additional whitespace
+ // background at the end of the line to represent the selected line break
+ if (bidi != null && selectionEnd > 0 && bidi.isRightToLeft(selectionEnd - 1)) {
+ int lineEndX = bidi.getTextWidth();
+ gc.fillRectangle(lineEndX - horizontalScrollOffset, paintY, lineEndSpaceWidth, lineHeight);
+ }
+ else {
+ selectionBackgroundWidth += lineEndSpaceWidth;
+ }
+ }
+ }
+ // handle empty line case
+ if (bidi != null && (paintX == 0)) {
+ paintX = xInset;
+ }
+ // fill the background first since expanded tabs are not
+ // drawn as spaces. tabs just move the draw position.
+ gc.fillRectangle(paintX - horizontalScrollOffset, paintY, selectionBackgroundWidth, lineHeight);
+}
+/**
+ * Draws the line at the specified location.
+ * <p>
+ *
+ * @param line the line to draw
+ * @param lineOffset offset of the first character in the line.
+ * Relative to the start of the document.
+ * @param renderOffset offset of the first character that should be rendered.
+ * Relative to the start of the line.
+ * @param styles the styles to use for rendering line segments. May be empty but not null.
+ * @param paintX x location to draw at
+ * @param paintY y location to draw at
+ * @param gc GC to draw on
+ * @param lineBackground line background color, used when no style is specified for a line segment.
+ * @param lineForeground line foreground color, used when no style is specified for a line segment.
+ * @param bidi the bidi object to use for measuring and rendering text in bidi locales.
+ * null when not in bidi mode.
+ */
+void drawStyledLine(String line, int lineOffset, int renderOffset, StyleRange[] styles, int paintX, int paintY, GC gc, Color lineBackground, Color lineForeground, StyledTextBidi bidi) {
+ int lineLength = line.length();
+ Color background = gc.getBackground();
+ Color foreground = gc.getForeground();
+ StyleRange style = null;
+ StyleRange[] filteredStyles = filterLineStyles(styles);
+ int renderStopX = getClientArea().width + horizontalScrollOffset;
+ FontData fontData = gc.getFont().getFontData()[0];
+
+ // Always render the entire line when in a bidi locale.
+ // Since we render the line in logical order we may start past the end
+ // of the visual right border of the client area and work towards the
+ // left.
+ for (int i = 0; i < styles.length && (paintX < renderStopX || bidi != null); i++) {
+ int styleLineLength;
+ int styleLineStart;
+ int styleLineEnd;
+ style = styles[i];
+ styleLineEnd = style.start + style.length - lineOffset;
+ styleLineStart = Math.max(style.start - lineOffset, 0);
+ // render unstyled text between the start of the current
+ // style range and the end of the previously rendered
+ // style range
+ if (styleLineStart > renderOffset) {
+ background = setLineBackground(gc, background, lineBackground);
+ foreground = setLineForeground(gc, foreground, lineForeground);
+ setLineFont(gc, fontData, SWT.NORMAL);
+ // don't try to render more text than requested
+ styleLineStart = Math.min(lineLength, styleLineStart);
+ paintX = drawText(line, lineOffset, renderOffset, styleLineStart - renderOffset, paintX, paintY, gc, bidi);
+ renderOffset = styleLineStart;
+ }
+ else
+ if (styleLineEnd <= renderOffset) {
+ // style ends before render start offset
+ // skip to the next style
+ continue;
+ }
+ if (styleLineStart >= lineLength) {
+ // there are line styles but no text for those styles
+ // possible when called with partial line text
+ break;
+ }
+ styleLineLength = Math.min(styleLineEnd, lineLength) - renderOffset;
+ // set style background color if specified
+ if (style.background != null) {
+ background = setLineBackground(gc, background, style.background);
+ foreground = setLineForeground(gc, foreground, style.background);
+ if (bidi != null) {
+ bidi.fillBackground(renderOffset, styleLineLength, -horizontalScrollOffset, paintY, lineHeight);
+ }
+ else {
+ int fillWidth = textWidth(line, lineOffset, renderOffset, styleLineLength, filteredStyles, paintX, gc, bidi);
+ gc.fillRectangle(paintX - horizontalScrollOffset, paintY, fillWidth, lineHeight);
+ }
+ }
+ else {
+ background = setLineBackground(gc, background, lineBackground);
+ }
+ // set style foreground color if specified
+ if (style.foreground != null) {
+ foreground = setLineForeground(gc, foreground, style.foreground);
+ }
+ else {
+ foreground = setLineForeground(gc, foreground, lineForeground);
+ }
+ setLineFont(gc, fontData, style.fontStyle);
+ paintX = drawText(line, lineOffset, renderOffset, styleLineLength, paintX, paintY, gc, bidi);
+ renderOffset += styleLineLength;
+ }
+ // render unstyled text at the end of the line
+ if ((style == null || renderOffset < lineLength) && (paintX < renderStopX || bidi != null)) {
+ setLineBackground(gc, background, lineBackground);
+ setLineForeground(gc, foreground, lineForeground);
+ setLineFont(gc, fontData, SWT.NORMAL);
+ drawText(line, lineOffset, renderOffset, lineLength - renderOffset, paintX, paintY, gc, bidi);
+ }
+}
+/**
+ * Draws the text at the specified location. Expands tabs to tab stops using
+ * the widget tab width.
+ * <p>
+ *
+ * @param text text to draw
+ * @param lineOffset offset of the first character in the line.
+ * Relative to the start of the document.
+ * @param startOffset offset of the first character in text to draw
+ * @param length number of characters to draw
+ * @param paintX x location to start drawing at
+ * @param paintY y location to draw at. Unused when draw is false
+ * @param gc GC to draw on
+ * location where drawing would stop
+ * @param bidi the bidi object to use for measuring and rendering text in bidi locales.
+ * null when not in bidi mode.
+ * @return x location where drawing stopped or 0 if the startOffset or
+ * length is outside the specified text.
+ */
+int drawText(String text, int lineOffset, int startOffset, int length, int paintX, int paintY, GC gc, StyledTextBidi bidi) {
+ int endOffset = startOffset + length;
+ int textLength = text.length();
+
+ if (startOffset < 0 || startOffset >= textLength || startOffset + length > textLength) {
+ return paintX;
+ }
+ for (int i = startOffset; i < endOffset; i++) {
+ int tabIndex = text.indexOf(TAB, i);
+ // is tab not present or past the rendering range?
+ if (tabIndex == -1 || tabIndex > endOffset) {
+ tabIndex = endOffset;
+ }
+ if (tabIndex != i) {
+ String tabSegment = text.substring(i, tabIndex);
+ if (bidi != null) {
+ paintX = bidi.drawBidiText(i, tabIndex - i, -horizontalScrollOffset, paintY);
+ }
+ else {
+ gc.drawString(tabSegment, paintX - horizontalScrollOffset, paintY, true);
+ paintX += gc.stringExtent(tabSegment).x;
+ if (tabIndex != endOffset && tabWidth > 0) {
+ paintX += tabWidth;
+ paintX -= paintX % tabWidth;
+ }
+ }
+ i = tabIndex;
+ }
+ else // is tab at current rendering offset?
+ if (tabWidth > 0 && isBidi() == false) {
+ paintX += tabWidth;
+ paintX -= paintX % tabWidth;
+ }
+ }
+ return paintX;
+}
+/**
+ * Ends the autoscroll process.
+ */
+void endAutoScroll() {
+ autoScrollDirection = SWT.NULL;
+}
+/**
+ * @param styles styles that may contain font styles.
+ * @return null if the styles contain only regular font styles, the
+ * unchanged styles otherwise.
+ */
+StyleRange[] filterLineStyles(StyleRange[] styles) {
+ if (styles != null) {
+ int styleIndex = 0;
+ while (styleIndex < styles.length && styles[styleIndex].fontStyle == SWT.NORMAL) {
+ styleIndex++;
+ }
+ if (styleIndex == styles.length) {
+ styles = null;
+ }
+ }
+ return styles;
+}
+/**
+ * Returns an array of bold text ranges for a line.
+ * <p>
+ * @param styles style ranges in the line, may be bold and non-bold
+ * @param lineOffset start index of the line, relative to the start of the document
+ * @param length of the line
+ * @return
+ * array[i] = bold start, relative to the start of the line
+ * array[i + 1] = bold length, no more than lineLength
+ * null if styles parameter is null
+ */
+int[] getBoldRanges(StyleRange[] styles, int lineOffset, int lineLength) {
+ int boldCount = 0;
+ int[] boldRanges = null;
+
+ if (styles == null) {
+ return null;
+ }
+ for (int i = 0; i < styles.length; i++) {
+ if (styles[i].fontStyle == SWT.BOLD) {
+ boldCount++;
+ }
+ }
+ if (boldCount > 0) {
+ boldRanges = new int[boldCount * 2];
+ boldCount = 0;
+ for (int i = 0; i < styles.length; i++) {
+ StyleRange style = styles[i];
+ if (style.fontStyle == SWT.BOLD) {
+ int styleEnd = Math.min(style.start + style.length - lineOffset, lineLength);
+ int styleStart = Math.max(0, style.start - lineOffset);
+ boldRanges[boldCount] = styleStart;
+ boldRanges[boldCount + 1] = styleEnd - styleStart;
+ boldCount += 2;
+ }
+ }
+ }
+ return boldRanges;
+}
+/**
+ * Returns the index of the last fully visible line.
+ * <p>
+ *
+ * @return index of the last fully visible line.
+ */
+int getBottomIndex() {
+ return Math.min(content.getLineCount(), topIndex + getLineCountWhole()) - 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 = new GC(this);
+ StyleRange[] styles = null;
+ StyledTextEvent event = getLineStyleData(lineOffset, line);
+
+ lineXOffset += horizontalScrollOffset;
+ if (event != null) {
+ styles = filterLineStyles(event.styles);
+ }
+ int low = -1;
+ int high = line.length();
+ while (high - low > 1) {
+ offset = (high + low) / 2;
+ int x = textWidth(line, lineOffset, 0, offset, styles, 0, gc, null);
+ int charWidth = textWidth(line, lineOffset, 0, offset + 1, styles, 0, gc, null) - 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 content;
+}
+/**
+ * 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;
+}
+/**
+ * Returns the horizontal scroll increment.
+ * <p>
+ *
+ * @return horizontal scroll increment.
+ */
+int getHorizontalIncrement() {
+ GC gc = new GC(this);
+ 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 (one or more of SWT.CTRL, SWT.SHIFT, SWT.ALT)
+ * @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 > content.getLineCount()) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ if (userLineBackground == false) {
+ lineBackground = defaultLineStyler.getLineBackground(index);
+ }
+ return lineBackground;
+}
+/**
+ * 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 are at least partially displayed in the widget client area.
+ * <p>
+ *
+ * @return number of lines that are at least partially displayed in the widget client area.
+ */
+int getLineCountTruncated() {
+ int lineCount;
+
+ if (lineHeight != 0) {
+ lineCount = (int) Math.ceil((float) getClientArea().height / lineHeight);
+ }
+ else {
+ lineCount = 1;
+ }
+ return lineCount;
+}
+
+/**
+ * Returns the number of lines that are completely displayed in the widget client area.
+ * <p>
+ *
+ * @return number of lines that are 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 content.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 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 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.
+ */
+StyledTextEvent getLineStyleData(int lineOffset, String line) {
+ if (isListening(LineGetStyle)) {
+ StyledTextEvent event = new StyledTextEvent(content);
+ event.detail = lineOffset;
+ event.text = line;
+ notifyListeners(LineGetStyle, event);
+ if (event.styles == null) {
+ event.styles = new StyleRange[0];
+ }
+ return event;
+ }
+ return null;
+}
+/**
+ * Returns the line background data for the given line or null if there is none.
+ */
+StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
+ if (isListening(LineGetBackground)) {
+ StyledTextEvent event = new StyledTextEvent(content);
+ event.detail = lineOffset;
+ event.text = line;
+ notifyListeners(LineGetBackground, event);
+ return event;
+ }
+ return null;
+}
+/**
+ * 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 = 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 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 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) {
+ int offset;
+ GC gc = new GC(this);
+ StyleRange[] styles = null;
+ StyledTextEvent event = getLineStyleData(lineOffset, line);
+
+ if (event != null) {
+ styles = filterLineStyles(event.styles);
+ }
+ lineXOffset += horizontalScrollOffset;
+ if (isBidi()) {
+ int[] boldStyles = getBoldRanges(styles, lineOffset, line.length());
+ StyledTextBidi bidi = new StyledTextBidi(gc, tabWidth, line, boldStyles, boldFont, getStyleOffsets (line, lineOffset));
+ offset = bidi.getOffsetAtX(lineXOffset);
+ }
+ else {
+ int low = -1;
+ int high = line.length();
+ while (high - low > 1) {
+ offset = (high + low) / 2;
+ if (lineXOffset <= textWidth(line, lineOffset, 0, offset + 1, styles, 0, gc, null)) {
+ 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 = (int) Math.ceil((float) 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 = content.getLineAtOffset(writer.getStart());
+ int endLine = content.getLineAtOffset(end);
+ String endLineText = content.getLine(endLine);
+ int endLineOffset = content.getOffsetAtLine(endLine);
+
+ for (int i = startLine; i <= endLine; i++) {
+ writer.writeLine(content.getLine(i), content.getOffsetAtLine(i));
+ if (i < endLine) {
+ writer.writeLineDelimiter(PlatformLineDelimiter);
+ }
+ }
+ if (end > endLineOffset + endLineText.length()) {
+ 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
+ * @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.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - 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);
+}
+/*
+Pseudo code for getSelectionLineStyles
+ for each style {
+ if (style ends before selection start) {
+ add style to list
+ }
+ else
+ if (style overlaps selection start (i.e., starts before selection start, ends after selection start) {
+ change style end
+ create new selection style with same font style starting at selection start ending at style end
+ add selection style
+ // does style extend beyond selection?
+ if (selection style end > selection end) {
+ selection style end = selection end
+ // preserve rest (unselected part) of old style
+ style start = selection end
+ style length = old style end - selection end
+ add style
+ }
+ }
+ else
+ if (style starts within selection) {
+ if (no selection style created) {
+ create selection style with regular font style, starting at selection start, ending at style start
+ add selection style
+ if (style start == selection start) {
+ set selection style font to style font
+ }
+ }
+ // gap between current selection style end and new style start?
+ if (style start > selection styke end && selection style font style != NORMAL) {
+ create selection style with regular font style, starting at selection style end, ending at style start
+ add selection style
+ }
+ if (selection style font != style font) {
+ selection style end = style start
+ add selection style
+ create selection style with style font style, starting at style start, ending at style end
+ }
+ else {
+ selection style end = style end
+ }
+ // does style extend beyond selection?
+ if (selection style end > selection end) {
+ selection style end = selection end
+ // preserve rest (unselected part) of old style
+ style start = selection end
+ style length = old style end - selection end
+ style start = selection end
+ add style
+ }
+ }
+ else {
+ if (no selection style created) {
+ create selection style with regular font style, starting at selection start, ending at selection end
+ add selection style
+ }
+ else
+ if (selection style end < selection end) {
+ if (selection style font style != NORMAL) {
+ create selection style with regular font style, starting at selection style end, ending at selection end
+ add selection style
+ }
+ else {
+ selection style end = selection end
+ }
+ }
+ add style
+ }
+ }
+ if (no selection style created) {
+ create selection style with regular font style, starting at selection start, ending at selection end
+ add selection style to list
+ }
+ else
+ if (selection style end < selection end) {
+ if (selection style font style != NORMAL) {
+ create selection style with regular font style, starting at selection style end, ending at selection end
+ add selection style
+ }
+ else {
+ selection style end = selection end
+ }
+ }
+*/
+/**
+ * Merges the selection into the styles that are passed in.
+ * The font style of existing style ranges is preserved in the selection.
+ * <p>
+ * @param styles the existing styles that the selection should be applied to.
+ * @return the selection style range merged with the existing styles
+ */
+StyleRange[] getSelectionLineStyles(StyleRange[] styles) {
+ int selectionStart = selection.x;
+ int selectionEnd = selection.y;
+ Vector newStyles = new Vector(styles.length);
+ StyleRange selectionStyle = null;
+ Color foreground = getSelectionForeground();
+ Color background = getSelectionBackground();
+
+ // potential optimization: ignore styles if there is no bold style and the entire line is selected
+ for (int i = 0; i < styles.length; i++) {
+ StyleRange style = styles[i];
+ int styleEnd = style.start + style.length;
+
+ if (styleEnd <= selectionStart) {
+ newStyles.addElement(style);
+ }
+ else // style overlaps selection start? (i.e., starts before selection start, ends after selection start
+ if (style.start < selectionStart && styleEnd > selectionStart) {
+ StyleRange newStyle = (StyleRange) style.clone();
+ newStyle.length -= styleEnd - selectionStart;
+ newStyles.addElement(newStyle);
+ // create new selection style with same font style starting at selection start ending at style end
+ selectionStyle = new StyleRange(selectionStart, styleEnd - selectionStart, foreground, background, newStyle.fontStyle);
+ newStyles.addElement(selectionStyle);
+ // if style extends beyond selection a new style is returned for the unselected part of the style
+ newStyle = setSelectionStyleEnd(selectionStyle, style);
+ if (newStyle != null) {
+ newStyles.addElement(newStyle);
+ }
+ }
+ else // style starts within selection?
+ if (style.start >= selectionStart && style.start < selectionEnd) {
+ StyleRange newStyle;
+ int selectionStyleEnd;
+ // no selection style created yet?
+ if (selectionStyle == null) {
+ // create selection style with regular font style, starting at selection start, ending at style start
+ selectionStyle = new StyleRange(selectionStart, style.start - selectionStart, foreground, background);
+ newStyles.addElement(selectionStyle);
+ if (style.start == selectionStart) {
+ selectionStyle.fontStyle = style.fontStyle;
+ }
+ }
+ selectionStyleEnd = selectionStyle.start + selectionStyle.length;
+ // gap between current selection style end and style start?
+ if (style.start > selectionStyleEnd && selectionStyle.fontStyle != SWT.NORMAL) {
+ // create selection style with regular font style, starting at selection style end, ending at style start
+ selectionStyle = new StyleRange(selectionStyleEnd, style.start - selectionStyleEnd, foreground, background);
+ newStyles.addElement(selectionStyle);
+ }
+ if (selectionStyle.fontStyle != style.fontStyle) {
+ // selection style end = style start
+ selectionStyle.length = style.start - selectionStyle.start;
+ // create selection style with style font style, starting at style start, ending at style end
+ selectionStyle = new StyleRange(style.start, style.length, foreground, background, style.fontStyle);
+ newStyles.addElement(selectionStyle);
+ }
+ else {
+ // selection style end = style end
+ selectionStyle.length = styleEnd - selectionStyle.start;
+ }
+ // if style extends beyond selection a new style is returned for the unselected part of the style
+ newStyle = setSelectionStyleEnd(selectionStyle, style);
+ if (newStyle != null) {
+ newStyles.addElement(newStyle);
+ }
+ }
+ else {
+ // no selection style created yet?
+ if (selectionStyle == null) {
+ // create selection style with regular font style, starting at selection start, ending at selection end
+ selectionStyle = new StyleRange(selectionStart, selectionEnd - selectionStart, foreground, background);
+ newStyles.addElement(selectionStyle);
+ }
+ else // does the current selection style end before the selection end?
+ if (selectionStyle.start + selectionStyle.length < selectionEnd) {
+ if (selectionStyle.fontStyle != SWT.NORMAL) {
+ int selectionStyleEnd = selectionStyle.start + selectionStyle.length;
+ // create selection style with regular font style, starting at selection style end, ending at selection end
+ selectionStyle = new StyleRange(selectionStyleEnd, selectionEnd - selectionStyleEnd, foreground, background);
+ newStyles.addElement(selectionStyle);
+ }
+ else {
+ selectionStyle.length = selectionEnd - selectionStyle.start;
+ }
+ }
+ newStyles.addElement(style);
+ }
+ }
+ if (selectionStyle == null) {
+ // create selection style with regular font style, starting at selection start, ending at selection end
+ selectionStyle = new StyleRange(selectionStart, selectionEnd - selectionStart, foreground, background);
+ newStyles.addElement(selectionStyle);
+ }
+ else // does the current selection style end before the selection end?
+ if (selectionStyle.start + selectionStyle.length < selectionEnd) {
+ if (selectionStyle.fontStyle != SWT.NORMAL) {
+ int selectionStyleEnd = selectionStyle.start + selectionStyle.length;
+ // create selection style with regular font style, starting at selection style end, ending at selection end
+ selectionStyle = new StyleRange(selectionStyleEnd, selectionEnd - selectionStyleEnd, foreground, background);
+ newStyles.addElement(selectionStyle);
+ }
+ else {
+ selectionStyle.length = selectionEnd - selectionStyle.start;
+ }
+ }
+ styles = new StyleRange[newStyles.size()];
+ newStyles.copyInto(styles);
+ return styles;
+}
+/**
+ * Returns the background color to be used for rendering selected text.
+ * <p>
+ *
+ * @return background color to be used for rendering selected text
+ */
+Color getSelectionBackground() {
+ return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
+}
+/**
+ * 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 foreground color to be used for rendering selected text.
+ * <p>
+ *
+ * @return foreground color to be used for rendering selected text
+ */
+Color getSelectionForeground() {
+ return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
+}
+/**
+ * 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 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>
+ *
+ * @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 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 < 0 || start > contentLength || end < 0 || end > contentLength || start > end) {
+ 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;
+}
+
+/**
+ * Gets the top index. The top index is the index of the fully visible line that
+ * is currently at the top of the widget. 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();
+
+ return topIndex;
+}
+
+/**
+ * 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 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 = content.getLineAtOffset(offset);
+ int lineOffset = content.getOffsetAtLine(line);
+ String lineText = content.getLine(line);
+ int lineLength = lineText.length();
+
+ if (offset >= getCharCount()) {
+ return offset;
+ }
+ if (offset == lineOffset + lineLength) {
+ line++;
+ offset = content.getOffsetAtLine(line);
+ }
+ else {
+ offset -= lineOffset;
+ char ch = lineText.charAt(offset);
+ boolean letterOrDigit = Character.isLetterOrDigit(ch);
+ while (offset < lineLength - 1 && Character.isLetterOrDigit(ch) == letterOrDigit) {
+ offset++;
+ ch = lineText.charAt(offset);
+ }
+ // skip over trailing whitespace
+ while (offset < lineLength - 1 && Character.isSpaceChar(ch)) {
+ offset++;
+ ch = lineText.charAt(offset);
+ }
+ if (offset == lineLength - 1 && (Character.isLetterOrDigit(ch) == letterOrDigit || Character.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 = content.getLineAtOffset(offset);
+ int lineOffset = content.getOffsetAtLine(line);
+ String lineText = content.getLine(line);
+ int lineLength = lineText.length();
+
+ if (offset >= getCharCount()) {
+ return offset;
+ }
+ if (offset == lineOffset + lineLength) {
+ line++;
+ offset = content.getOffsetAtLine(line);
+ }
+ else {
+ offset -= lineOffset;
+ char ch = lineText.charAt(offset);
+ boolean letterOrDigit = Character.isLetterOrDigit(ch);
+
+ while (offset < lineLength - 1 && Character.isLetterOrDigit(ch) == letterOrDigit && Character.isSpaceChar(ch) == false) {
+ offset++;
+ ch = lineText.charAt(offset);
+ }
+ if (offset == lineLength - 1 && Character.isLetterOrDigit(ch) == letterOrDigit && Character.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 = content.getLineAtOffset(offset);
+ int lineOffset = content.getOffsetAtLine(line);
+ String lineText = content.getLine(line);
+
+ if (offset <= 0) {
+ return offset;
+ }
+ if (offset == lineOffset) {
+ line--;
+ lineText = content.getLine(line);
+ offset = content.getOffsetAtLine(line) + lineText.length();
+ }
+ else {
+ char ch;
+ boolean letterOrDigit;
+
+ offset -= lineOffset;
+ // skip over trailing whitespace
+ do {
+ offset--;
+ ch = lineText.charAt(offset);
+ } while (offset > 0 && Character.isSpaceChar(ch));
+ letterOrDigit = Character.isLetterOrDigit(ch);
+ while (offset > 0 && Character.isLetterOrDigit(ch) == letterOrDigit && Character.isSpaceChar(ch) == false) {
+ offset--;
+ ch = lineText.charAt(offset);
+ }
+ if (offset > 0 || Character.isLetterOrDigit(ch) != letterOrDigit) {
+ offset++;
+ }
+ offset += lineOffset;
+ }
+ return offset;
+}
+
+/**
+ * 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 give offset in the line.
+ */
+int getXAtOffset(String line, int lineIndex, int lineOffset) {
+ int x;
+
+ if (lineOffset == 0 && isBidi() == false) {
+ x = 0;
+ }
+ else {
+ GC gc = new GC(this);
+ x = textWidth(line, lineIndex, Math.min(line.length(), lineOffset), gc);
+ 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);
+ }
+ };
+ 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(content);
+ 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) {
+ // do nothing
+ // hooked to disable automatic tab traversal on tab key press
+ }
+ });
+ 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);
+ }
+ });
+ }
+}
+/**
+ * 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 area will be cleared before anything
+ * is redrawn. 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
+ * area have changed, clearBackground should be set to true. If only foreground colors
+ * have changed for the redraw area, 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.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - 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();
+ String rtfText = null;
+ int length = getCharCount();
+
+ if (length > 0) {
+ RTFWriter rtfWriter = new RTFWriter(0, length);
+ rtfText = getPlatformDelimitedText(rtfWriter);
+ }
+ return rtfText;
+}
+/**
+ * Frees resources.
+ */
+void handleDispose() {
+ Enumeration colors;
+
+ clipboard.dispose();
+ ibeamCursor.dispose();
+ if (boldFont != null) {
+ boldFont.dispose();
+ }
+ if (content != null) {
+ content.removeTextChangeListener(textChangeListener);
+ }
+ if (leftCaretBitmap != null) {
+ leftCaretBitmap.dispose();
+ }
+ if (rightCaretBitmap != null) {
+ rightCaretBitmap.dispose();
+ }
+ if (isBidi()) {
+ StyledTextBidi.removeLanguageListener(this.handle);
+ }
+}
+/**
+ * Updates the caret location and selection if mouse button 1 has been
+ * pressed.
+ */
+void handleMouseDoubleClick(Event event) {
+ if (event.button != 1 || doubleClickEnabled == false) {
+ return;
+ }
+ mouseDoubleClick = true;
+ caretOffset = getWordEndNoSpaces(caretOffset);
+ resetSelection();
+ caretOffset = getWordStart(caretOffset);
+ showCaret();
+ doMouseSelection();
+}
+/**
+ * Updates the caret location and selection if mouse button 1 has been
+ * pressed.
+ */
+void handleMouseDown(Event event) {
+ if (event.button != 1) {
+ return;
+ }
+ mouseDoubleClick = false;
+ doMouseLocationChange(event.x, event.y, (event.stateMask & SWT.SHIFT) != 0);
+}
+/**
+ * Autoscrolling ends when the mouse button is released.
+ */
+void handleMouseUp(Event event) {
+ endAutoScroll();
+}
+/**
+ * Updates the caret location and selection if mouse button 1 is pressed
+ * during the mouse move.
+ */
+void handleMouseMove(Event event) {
+ if (mouseDoubleClick == true || (event.stateMask & SWT.BUTTON1) == 0) {
+ return;
+ }
+ doMouseLocationChange(event.x, event.y, true);
+ doAutoScroll(event);
+ // caret location only needs to be updated if we are not autoscrolling
+// if (autoScrollDirection == SWT.NULL) {
+// doMouseLocationChange(event.x, event.y, true);
+// }
+}
+/**
+ * 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);
+ }
+}
+/**
+ * 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();
+ StyledTextPrinter.print(this);
+}
+ /**
+ * 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>
+ */
+public Runnable print(Printer printer) {
+ checkWidget();
+ return new StyledTextPrinter(this, printer);
+}
+
+/**
+ * Scrolls the widget horizontally.
+ */
+void handleHorizontalScroll(Event event) {
+ int scrollPixel = getHorizontalBar().getSelection() - horizontalScrollOffset;
+ scrollHorizontal(scrollPixel);
+}
+
+/**
+ * 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);
+ }
+}
+
+/**
+ * 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) {
+ action = getKeyBinding(event.keyCode | event.stateMask);
+ }
+ else {
+ action = getKeyBinding(event.character | event.stateMask);
+ }
+ if (action == SWT.NULL) {
+ // ignore anything below SPACE and ignore DEL
+ if (event.character > 31 && event.character != SWT.DEL ||
+ event.character == SWT.CR || event.character == SWT.LF ||
+ event.character == TAB) {
+ doContent(event.character);
+ }
+ }
+ else {
+ invokeAction(action);
+ }
+}
+/**
+ * Renders the invalidated area specified in the paint event.
+ * <p>
+ *
+ * @param event paint event
+ */
+void handlePaint(Event event) {
+ int startLine = (event.y + verticalScrollOffset) / lineHeight;
+ int paintYFromTopLine = (startLine - topIndex) * lineHeight;
+ int topLineOffset = topIndex * lineHeight - verticalScrollOffset;
+ int startY = paintYFromTopLine + topLineOffset; // adjust y position for pixel based scrolling
+ int renderHeight = event.y + event.height - startY;
+ int paintY = 0;
+ int lineCount = content.getLineCount();
+ Rectangle clientArea = getClientArea();
+ Color background = getBackground();
+ Color foreground = getForeground();
+ Image lineBuffer;
+ GC lineGC;
+
+ // 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;
+ }
+ if (isSingleLine()) {
+ lineCount = 1;
+ if (startLine > 1) {
+ startLine = 1;
+ }
+ }
+ lineBuffer = new Image(getDisplay(), clientArea.width, renderHeight);
+ lineGC = new GC(lineBuffer);
+ lineGC.setFont(event.gc.getFont());
+ lineGC.setForeground(foreground);
+ lineGC.setBackground(background);
+ for (int i = startLine; paintY < renderHeight && i < lineCount; i++, paintY += lineHeight) {
+ String line = content.getLine(i);
+ 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);
+ }
+ event.gc.drawImage(lineBuffer, 0, startY);
+ lineGC.dispose();
+ lineBuffer.dispose();
+}
+/**
+ * Recalculates the scroll bars.
+ * <p>
+ *
+ * @param event resize event
+ */
+void handleResize(Event event) {
+ ScrollBar verticalBar = getVerticalBar();
+ int oldHeight = clientAreaHeight;
+
+ clientAreaHeight = getClientArea().height;
+ if (clientAreaHeight > oldHeight) {
+ int lineCount = content.getLineCount();
+ int oldBottomIndex = topIndex + oldHeight / lineHeight;
+ int newItemCount = (int) Math.ceil((float) (clientAreaHeight - oldHeight) / lineHeight);
+ oldBottomIndex = Math.min(oldBottomIndex, lineCount);
+ newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex);
+ calculateContentWidth(oldBottomIndex, newItemCount);
+ }
+ setScrollBars();
+ claimBottomFreeSpace();
+ claimRightFreeSpace();
+}
+/**
+ * Updates the caret position and selection and the scroll bars to reflect
+ * the content change.
+ * <p>
+ */
+void handleTextChanged(TextChangedEvent event) {
+ int clientAreaHeight = getClientArea().height;
+ int visibleItemCount = (int) Math.ceil((float) clientAreaHeight / lineHeight);
+ int firstLine = content.getLineAtOffset(lastTextChangeStart);
+ int stopLine;
+
+ // calculate width of visible changed lines
+ stopLine = firstLine + lastTextChangeNewLineCount + 1;
+ if (stopLine > topIndex && firstLine < topIndex + visibleItemCount) {
+ int startLine = Math.max(firstLine, topIndex);
+ calculateContentWidth(
+ startLine,
+ Math.min(stopLine, topIndex + visibleItemCount) - startLine);
+ }
+ 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();
+ }
+}
+/**
+ * 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;
+ if (isMultiLineChange) {
+ redrawMultiLineChange(textChangeY, event.newLineCount, event.replaceLineCount);
+ }
+ else {
+ redraw(0, textChangeY, getClientArea().width, lineHeight, true);
+ }
+ // notify default line styler about text change
+ if (defaultLineStyler != null) {
+ defaultLineStyler.textChanging(event);
+ }
+}
+/**
+ * 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();
+}
+/**
+ * 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 initializeFonts() {
+ FontData fontData;
+ GC gc = new GC(this);
+
+ lineEndSpaceWidth = gc.stringExtent(" ").x;
+ regularFont = getFont();
+ fontData = regularFont.getFontData()[0];
+ fontData.setStyle(fontData.getStyle() | SWT.BOLD);
+ boldFont = new Font(getDisplay(), fontData);
+ gc.dispose();
+}
+/**
+ * Executes the action.
+ * <p>
+ *
+ * @param action one of the actions defined in ST.java
+ */
+public void invokeAction(int action) {
+ checkWidget();
+ switch (action) {
+ // Navigation
+ case ST.LINE_UP:
+ doLineUp();
+ clearSelection(true);
+ break;
+ case ST.LINE_DOWN:
+ doLineDown();
+ 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:
+ doLineUp();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_LINE_DOWN:
+ doSelectionLineDown();
+ doSelection(SWT.RIGHT);
+ showCaret();
+ 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:
+ doPageUp();
+ doSelection(SWT.LEFT);
+ break;
+ case ST.SELECT_PAGE_DOWN:
+ doPageDown(true);
+ 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();
+ clearSelection(true);
+ break;
+ case ST.DELETE_NEXT:
+ doDelete();
+ clearSelection(true);
+ break;
+ // Miscellaneous
+ case ST.TOGGLE_OVERWRITE:
+ overwrite = !overwrite; // toggle insert/overwrite mode
+ break;
+ }
+}
+/**
+ * Temporary until SWT provides this
+ */
+boolean isBidi() {
+ String codePage = System.getProperty("file.encoding").toUpperCase();
+ boolean isWindows = System.getProperty("os.name").startsWith("Windows");
+
+ // Running on Windows and with Hebrew or Arabic codepage?
+ return isWindows && ("CP1255".equals(codePage) || "CP1256".equals(codePage));
+}
+/**
+ * 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) {
+ return false;
+// 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 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;
+ if (isListening(ExtendedModify)) {
+ styledTextEvent = new StyledTextEvent(content);
+ 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) {
+ internalSetSelection(event.start + event.text.length(), 0);
+ // always update the caret location. fixes 1G8FODP
+ if (isBidi()) {
+ lastCaretDirection = SWT.NULL;
+ showBidiCaret();
+ }
+ else {
+ showCaret();
+ }
+ }
+ notifyListeners(SWT.Modify, event);
+ if (isListening(ExtendedModify)) {
+ notifyListeners(ExtendedModify, styledTextEvent);
+ }
+ }
+}
+/**
+ * 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 area, false=draw the foreground directly without invalidating the
+ * redraw area.
+ */
+void redrawBidiLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
+ Rectangle clientArea = getClientArea();
+ int lineCount = lastLine - firstLine + 1;
+ int redrawY = firstLine * lineHeight - verticalScrollOffset;
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ String line = content.getLine(firstLine);
+ GC gc = new GC(this);
+ StyleRange[] styles = null;
+ StyledTextEvent event = getLineStyleData(firstLineOffset, line);
+ StyledTextBidi bidi;
+ int[] boldStyles;
+
+ if (event != null) {
+ styles = event.styles;
+ }
+ boldStyles = getBoldRanges(styles, firstLineOffset, line.length());
+ bidi = new StyledTextBidi(gc, tabWidth, line, boldStyles, boldFont, getStyleOffsets (line, firstLineOffset));
+ bidi.redrawRange(this, offsetInFirstLine, Math.min(line.length(), endOffset) - offsetInFirstLine, -horizontalScrollOffset, redrawY, 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 lineBreakStartX = bidi.getTextWidth();
+ // handle empty line case
+ if (lineBreakStartX == 0) lineBreakStartX = xInset;
+ lineBreakStartX = lineBreakStartX - horizontalScrollOffset;
+ int lineBreakWidth;
+ if ((getStyle() & SWT.FULL_SELECTION) != 0) {
+ lineBreakWidth = clientArea.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;
+ event = getLineStyleData(lastLineOffset, line);
+ if (event != null) {
+ styles = event.styles;
+ }
+ else {
+ styles = null;
+ }
+ boldStyles = getBoldRanges(styles, lastLineOffset, line.length());
+ bidi = new StyledTextBidi(gc, tabWidth, line, boldStyles, boldFont, getStyleOffsets (line, lastLineOffset));
+ bidi.redrawRange(this, 0, offsetInLastLine, -horizontalScrollOffset, redrawY, lineHeight);
+ }
+ }
+ gc.dispose();
+}
+/**
+ * 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 area, false=draw the foreground directly without invalidating the
+ * redraw area.
+ */
+void redrawLines(int firstLine, int offsetInFirstLine, int lastLine, int endOffset, boolean clearBackground) {
+ Rectangle clientArea = getClientArea();
+ String line = content.getLine(firstLine);
+ int lineCount = lastLine - firstLine + 1;
+ int redrawX = getXAtOffset(line, firstLine, offsetInFirstLine);
+ int redrawStopX;
+ int redrawY = firstLine * lineHeight - verticalScrollOffset;
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+
+ // calculate redraw stop location
+ if ((getStyle() & SWT.FULL_SELECTION) != 0 && lastLine > firstLine) {
+ redrawStopX = clientArea.width;
+ }
+ else {
+ redrawStopX = getXAtOffset(line, firstLine, endOffset - firstLineOffset);
+ }
+ 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);
+ redrawStopX = getXAtOffset(line, lastLine, offsetInLastLine);
+ 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
+ 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
+ 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 area will be cleared before anything
+ * is redrawn. 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
+ * area have changed, clearBackground should be set to true. If only foreground colors
+ * have changed for the redraw area, clearBackground can be set to false.
+ */
+public void redrawRange(int start, int length, boolean clearBackground) {
+ checkWidget();
+ internalRedrawRange(start, length, clearBackground);
+}
+/**
+ * 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 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>
+ * </ul>
+ */
+public void replaceTextRange(int start, int length, String text) {
+ checkWidget();
+ int contentLength = getCharCount();
+ int end = start + length;
+ Event event = new Event();
+
+ if (start < 0 || start > contentLength || end < 0 || end > contentLength || start > end) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ 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;
+ verticalScrollOffset = 0;
+ horizontalScrollOffset = 0;
+ contentWidth = 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();
+ setCaret();
+ 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();
+ scroll(
+ pixels * -1, 0, // destination x, y
+ 0, 0, // source x, y
+ clientArea.width, clientArea.height, true);
+ horizontalScrollOffset += pixels;
+ setCaret();
+}
+/**
+ * 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
+ */
+void scrollHorizontalBar(int pixels) {
+ if (pixels == 0) {
+ return;
+ }
+ ScrollBar horizontalBar = getHorizontalBar();
+ if (horizontalBar != null) {
+ horizontalBar.setSelection(horizontalScrollOffset + pixels);
+ }
+ scrollHorizontal(pixels);
+}
+/**
+ * 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);
+}
+void setCaret () {
+ if (isBidi()) {
+ setBidiCaret(false);
+ }
+ else {
+ setCaretLocation();
+ }
+}
+/**
+ * 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();
+ }
+}
+/**
+ * Moves the Caret to the current caret offset and switches the
+ * keyboard according to the input part.
+ */
+void setBidiCaret(boolean setKeyboard) {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineStartOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineStartOffset;
+ String lineText = content.getLine(line);
+ GC gc = new GC(this);
+ StyledTextEvent event = getLineStyleData(lineStartOffset, lineText);
+ StyledTextBidi bidi;
+ int[] boldStyles = null;
+ Caret caret;
+
+ if (event != null) {
+ boldStyles = getBoldRanges(event.styles, lineStartOffset, lineText.length());
+ }
+ bidi = new StyledTextBidi(gc, tabWidth, lineText, boldStyles, boldFont, getStyleOffsets (lineText, lineStartOffset));
+ if (setKeyboard) {
+ if (offsetInLine > 0 && bidi.isRightToLeft(offsetInLine - 1) != bidi.isRightToLeft(offsetInLine)) {
+ // continue with previous character type
+ bidi.setKeyboardLanguage(offsetInLine - 1);
+ }
+ else {
+ bidi.setKeyboardLanguage(offsetInLine);
+ }
+ }
+ caret = getCaret();
+ if (caret != null) {
+ int caretX;
+ if (lastCaretDirection == SWT.NULL) {
+ caretX = bidi.getCaretPosition(offsetInLine);
+ } else {
+ caretX = bidi.getCaretPosition(offsetInLine, lastCaretDirection);
+ }
+ caretX = caretX - horizontalScrollOffset;
+ if (StyledTextBidi.getKeyboardLanguageDirection() == SWT.RIGHT) {
+ caretX -= (getCaretWidth() - 1);
+ }
+ createBidiCaret();
+ caret.setLocation(caretX, line * lineHeight - verticalScrollOffset);
+ }
+ gc.dispose();
+}
+/**
+ * Moves the Caret to the current caret offset.
+ */
+void setBidiCaretLocation() {
+ // set the caret location, also set the keyboard
+ setBidiCaret(true);
+}
+/**
+ * Moves the Caret to the current caret offset.
+ */
+void setCaretLocation() {
+ if (isBidi()) {
+ setBidiCaretLocation();
+ }
+ else {
+ Caret caret = getCaret();
+ if (caret != null) {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineStartOffset = content.getOffsetAtLine(line);
+ int caretX = getXAtOffset(content.getLine(line), line, caretOffset - lineStartOffset);
+ caret.setLocation(caretX, line * lineHeight - verticalScrollOffset);
+ }
+ }
+}
+/**
+ * Sets the caret offset.
+ * <p>
+ * <b>NOTE:</b> If offset is greater than the number of characters of text in the
+ * widget, the value will be ignored.
+ * </p>
+ *
+ * @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();
+}
+/**
+ * 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 content) {
+ checkWidget();
+ if (content == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ if (this.content != null) {
+ this.content.removeTextChangeListener(textChangeListener);
+ }
+ this.content = content;
+ content.addTextChangeListener(textChangeListener);
+ reset();
+}
+/**
+ * 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();
+
+ super.setFont(font);
+ if (boldFont != null) {
+ boldFont.dispose();
+ }
+ initializeFonts();
+ calculateLineHeight();
+ contentWidth = 0;
+ calculateContentWidth();
+ calculateScrollBars();
+ setTabs(getTabs());
+ if (isBidi()) {
+ caretDirection = SWT.NULL;
+ createCaretBitmaps();
+ createBidiCaret();
+ setCaret();
+ }
+ else {
+ Caret caret = getCaret();
+ if (caret != null) {
+ caret.setSize(caret.getSize().x, lineHeight);
+ }
+ }
+ 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) {
+ // 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 > contentWidth - clientAreaWidth) {
+ offset = Math.max(0, contentWidth - clientAreaWidth);
+ }
+ }
+ scrollHorizontalBar(offset - horizontalScrollOffset);
+}
+/**
+ * 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>
+ * 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. 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 > content.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;
+ redraw(
+ 0, startLine * lineHeight,
+ getClientArea().width, lineCount * lineHeight, true);
+}
+/**
+ * Sets the background of the specified GC for a line rendering operation,
+ * if it is not already set.
+ * </p>
+ *
+ * @param gc GC to set the background color in
+ * @param currentBackground background color currently set in gc
+ * @param newBackground new background color of gc
+ */
+Color setLineBackground(GC gc, Color currentBackground, Color newBackground) {
+ if (currentBackground.equals(newBackground) == false) {
+ gc.setBackground(newBackground);
+ }
+ return newBackground;
+}
+
+/**
+ * Sets the font of the specified GC if it is not already set.
+ * </p>
+ *
+ * @param gc GC to set the font in
+ * @param currentFont font data of font currently set in gc
+ * @param style desired style of the font in gc. Can be one of
+ * SWT.NORMAL, SWT.ITALIC, SWT.BOLD
+ */
+void setLineFont(GC gc, FontData currentFont, int style) {
+ if (currentFont.getStyle() != style) {
+ if (style == SWT.BOLD) {
+ currentFont.setStyle(style);
+ gc.setFont(boldFont);
+ }
+ else
+ if (style == SWT.NORMAL) {
+ currentFont.setStyle(style);
+ gc.setFont(regularFont);
+ }
+ }
+}
+/**
+ * Sets the foreground of the specified GC for a line rendering operation,
+ * if it is not already set.
+ * </p>
+ *
+ * @param gc GC to set the foreground color in
+ * @param currentForeground foreground color currently set in gc
+ * @param newForeground new foreground color of gc
+ */
+Color setLineForeground(GC gc, Color currentForeground, Color newForeground) {
+ if (currentForeground.equals(newForeground) == false) {
+ gc.setForeground(newForeground);
+ }
+ return newForeground;
+}
+/**
+ * Adjusts the scroll bar maximum and page size to reflect content
+ * width/length changes.
+ */
+void setScrollBars() {
+ ScrollBar verticalBar = getVerticalBar();
+ ScrollBar horizontalBar = getHorizontalBar();
+ Rectangle clientArea = getClientArea();
+ final int INACTIVE = 1;
+
+ if (verticalBar != null) {
+ 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);
+ }
+ }
+ if (horizontalBar != null) {
+ // 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 < contentWidth) {
+ horizontalBar.setValues(
+ horizontalBar.getSelection(),
+ horizontalBar.getMinimum(),
+ contentWidth, // maximum
+ clientArea.width, // thumb size
+ horizontalBar.getIncrement(),
+ clientArea.width); // page size
+ }
+ else
+ if (horizontalBar.getThumb() != INACTIVE || horizontalBar.getMaximum() != INACTIVE) {
+ horizontalBar.setValues(
+ horizontalBar.getSelection(),
+ horizontalBar.getMinimum(),
+ INACTIVE,
+ INACTIVE,
+ horizontalBar.getIncrement(),
+ INACTIVE);
+ }
+ }
+}
+/**
+ * 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) {
+ 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
+ * @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 and 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 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
+ * @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 and 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();
+ int contentLength = getCharCount();
+
+ if (start > end || start < 0 || 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, end - start);
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+ 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, start <= start + length <= getCharCount()
+ * must be true.
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - 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 > end || start < 0 || 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);
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+}
+/**
+ * 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, start <= start + length
+ * <= getCharCount() must be true.
+ */
+void internalSetSelection(int start, int length) {
+ int end = start + length;
+
+ if (selection.x != start || selection.y != end) {
+ clearSelection(false);
+ selectionAnchor = selection.x = start;
+ caretOffset = selection.y = end;
+ if (length > 0) {
+ 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();
+ boolean redrawFirstLine = false;
+ boolean redrawLastLine = false;
+
+ // 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) {
+ // 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 rangeEnd = range.start + range.length;
+ int firstLine = content.getLineAtOffset(range.start);
+ int lastLine = content.getLineAtOffset(rangeEnd);
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ if (isStyleChanging(range, range.start, Math.min(rangeEnd, firstLineOffset + content.getLine(firstLine).length()))) {
+ redrawFirstLine = true;
+ }
+ if (lastLine != firstLine) {
+ int lastLineOffset = content.getOffsetAtLine(lastLine);
+ if (isStyleChanging(range, lastLineOffset, rangeEnd)) {
+ redrawLastLine = true;
+ }
+ }
+ }
+ if (isBidi()) {
+ redrawFirstLine = true;
+ redrawLastLine = true;
+ }
+ defaultLineStyler.setStyleRange(range);
+ if (range != null) {
+ internalRedrawRange(range.start, range.length, true);
+ if (redrawFirstLine) {
+ // redraw starting at the style change start offset since
+ // single line text changes, followed by style changes will
+ // flash otherwise
+ int firstLine = content.getLineAtOffset(range.start);
+ int firstLineOffset = content.getOffsetAtLine(firstLine);
+ String firstLineText = content.getLine(firstLine);
+ int redrawX = getXAtOffset(firstLineText, firstLine, range.start - firstLineOffset);
+ int redrawY = firstLine * lineHeight - verticalScrollOffset;
+ redraw(redrawX, redrawY, getClientArea().width, lineHeight, true);
+ }
+ if (redrawLastLine) {
+ // redraw the whole line if the font style changed on the last line
+ int lastLine = content.getLineAtOffset(range.start + range.length);
+ int redrawY = lastLine * lineHeight - verticalScrollOffset;
+ redraw(0, redrawY, getClientArea().width, lineHeight, true);
+ }
+ }
+ else {
+ redraw();
+ }
+ // make sure that the caret is positioned correctly.
+ // caret location may change if font style changes.
+ // fixes 1G8FODP
+ setCaret();
+}
+
+/**
+ * Sets styles to be used for rendering the widget content. All styles
+ * 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];
+ if (last.start + last.length > content.getCharCount()) {
+ SWT.error(SWT.ERROR_INVALID_RANGE);
+ }
+ }
+ 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
+ setCaret();
+}
+/**
+ * Ensures that the selection style ends at the selection end.
+ * <code>selectionStyle</code> is assumed to be created based on the style
+ * range of <code>style</code>. If <code>selectionStyle</code> does extend
+ * beyond the selection range a new style is returned to preserve the style
+ * passed in with <code>style</code>.
+ * <p>
+ * @param selectionStyle the selection style based on the style range in
+ * <code>style</code>
+ * @param style the existing style that is to be merged with the selection
+ * @return a new style that preserves the style passed in with <code>style</code>
+ * if the selection does not fully extend over the existing style range.
+ * null otherwise.
+ */
+StyleRange setSelectionStyleEnd(StyleRange selectionStyle, StyleRange style) {
+ int selectionEnd = selection.y;
+ StyleRange newStyle = null;
+
+ // does style extend beyond selection?
+ if (selectionStyle.start + selectionStyle.length > selectionEnd) {
+ int styleEnd = style.start + style.length;
+ selectionStyle.length = selectionEnd - selectionStyle.start;
+ // preserve rest (unselected part) of old style
+ newStyle = (StyleRange) style.clone();
+ newStyle.start = selectionEnd;
+ newStyle.length = styleEnd - selectionEnd;
+ }
+ return newStyle;
+}
+/**
+ * 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;
+ calculateTabWidth();
+ if (caretOffset > 0) {
+ caretOffset = 0;
+ if (isBidi()) {
+ showBidiCaret();
+ }
+ else {
+ showCaret();
+ }
+ clearSelection(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>
+ */
+public void setText(String text) {
+ checkWidget();
+ Event event = new Event();
+
+ event.start = 0;
+ event.end = getCharCount();
+ event.text = text;
+ event.doit = true;
+ notifyListeners(SWT.Verify, event);
+ if (event.doit) {
+ String replacedText = content.getTextRange(event.start, event.end - event.start);
+ content.setText(event.text);
+ event.end = event.start + event.text.length();
+ event.text = replacedText;
+ notifyListeners(SWT.Modify, event);
+ }
+}
+/**
+ * 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() -
+ * visible lines per page. 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 = content.getLineCount();
+ int pageSize = Math.min(lineCount, getLineCountWhole());
+
+ if (getCharCount() == 0) {
+ return;
+ }
+ if (topIndex < 0) {
+ topIndex = 0;
+ }
+ else
+ if (topIndex > lineCount - pageSize) {
+ topIndex = lineCount - pageSize;
+ }
+ setVerticalScrollOffset(topIndex * getVerticalIncrement(), true);
+ // set the top index directly in case setVerticalScrollOffset didn't
+ // (ie. because the widget is not yet visible)
+ this.topIndex = topIndex;
+}
+/**
+ * Scrolls the widget vertically.
+ * <p>
+ *
+ * @param pixels number of pixels to scroll. > 0 = scroll up, < 0 scroll down
+ */
+void setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) {
+ Rectangle clientArea;
+ ScrollBar verticalBar = getVerticalBar();
+ int verticalIncrement = getVerticalIncrement();
+
+ if (pixelOffset == verticalScrollOffset) {
+ return;
+ }
+ 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);
+ if (verticalIncrement != 0) {
+ int oldTopIndex = topIndex;
+
+ topIndex = (int) Math.ceil((float) pixelOffset / verticalIncrement);
+ if (topIndex != oldTopIndex) {
+ int lineCount = content.getLineCount();
+ int visibleItemCount = (int) Math.ceil((float) clientArea.height / verticalIncrement);
+ int oldBottomIndex = Math.min(oldTopIndex + visibleItemCount, lineCount);
+ int newItemCount = topIndex - oldTopIndex;
+
+ if (Math.abs(newItemCount) > visibleItemCount) {
+ calculateContentWidth();
+ }
+ else {
+ if (newItemCount > 0) {
+ newItemCount = Math.min(newItemCount, lineCount - oldBottomIndex);
+ calculateContentWidth(oldBottomIndex, newItemCount);
+ }
+ else
+ if (newItemCount < 0) {
+ // make sure calculation range does not exceed number of lines
+ // fixes 1GBKCLF
+ calculateContentWidth(topIndex, Math.min(newItemCount * -1, lineCount - topIndex));
+ }
+ }
+ setScrollBars();
+ }
+ }
+ verticalScrollOffset = pixelOffset;
+ setCaret();
+}
+
+boolean scrollBidiCaret() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+ String lineText = content.getLine(line);
+ int xAtOffset = getXAtOffset(lineText, line, offsetInLine);
+ int clientAreaWidth = getClientArea().width;
+ int verticalIncrement = getVerticalIncrement();
+ int horizontalIncrement = clientAreaWidth / 4;
+ boolean scrolled = false;
+
+ if (xAtOffset < 0) {
+ // always make 1/4 of a page visible
+ xAtOffset = Math.max(horizontalScrollOffset * -1, xAtOffset - horizontalIncrement);
+ scrollHorizontalBar(xAtOffset);
+ scrolled = true;
+ }
+ else
+ if (xAtOffset > clientAreaWidth) {
+ // always make 1/4 of a page visible
+ xAtOffset = Math.min(contentWidth - horizontalScrollOffset, xAtOffset + horizontalIncrement);
+ scrollHorizontalBar(xAtOffset - clientAreaWidth);
+ scrolled = true;
+ }
+ if (line < topIndex) {
+ setVerticalScrollOffset(line * verticalIncrement, true);
+ scrolled = true;
+ }
+ else
+ if (line > getBottomIndex()) {
+ setVerticalScrollOffset((line - getBottomIndex()) * verticalIncrement + verticalScrollOffset, true);
+ scrolled = true;
+ }
+ return scrolled;
+}
+/**
+ * Sets the caret location and scrolls the caret offset into view.
+ */
+void showBidiCaret() {
+ boolean scrolled = scrollBidiCaret();
+ if (scrolled == false) {
+ setCaret();
+ }
+}
+/**
+ * Sets the caret location and scrolls the caret offset into view.
+ */
+void showCaret() {
+ int line = content.getLineAtOffset(caretOffset);
+ int lineOffset = content.getOffsetAtLine(line);
+ int offsetInLine = caretOffset - lineOffset;
+ String lineText = content.getLine(line);
+ int xAtOffset = getXAtOffset(lineText, line, offsetInLine);
+ int clientAreaWidth = getClientArea().width;
+ int verticalIncrement = getVerticalIncrement();
+ int horizontalIncrement = clientAreaWidth / 4;
+ boolean scrolled = false;
+
+ if (xAtOffset < 0) {
+ // always make 1/4 of a page visible
+ xAtOffset = Math.max(horizontalScrollOffset * -1, xAtOffset - horizontalIncrement);
+ scrollHorizontalBar(xAtOffset);
+ scrolled = true;
+ }
+ else
+ if (xAtOffset > clientAreaWidth) {
+ // always make 1/4 of a page visible
+ xAtOffset = Math.min(contentWidth - horizontalScrollOffset, xAtOffset + horizontalIncrement);
+ scrollHorizontalBar(xAtOffset - clientAreaWidth);
+ scrolled = true;
+ }
+ if (line < topIndex) {
+ setVerticalScrollOffset(line * verticalIncrement, true);
+ scrolled = true;
+ }
+ else
+ if (line > getBottomIndex()) {
+ setVerticalScrollOffset((line - getBottomIndex()) * verticalIncrement + verticalScrollOffset, true);
+ scrolled = true;
+ }
+ if (scrolled == false) {
+ setCaretLocation();
+ }
+}
+/**
+ * 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);
+ int clientAreaWidth = getClientArea().width;
+ int verticalIncrement = getVerticalIncrement();
+ int horizontalIncrement = clientAreaWidth / 4;
+
+ if (xAtOffset < 0) {
+ // always make 1/4 of a page visible
+ xAtOffset = Math.max(horizontalScrollOffset * -1, xAtOffset - horizontalIncrement);
+ scrollHorizontalBar(xAtOffset);
+ }
+ else
+ if (xAtOffset > clientAreaWidth) {
+ // always make 1/4 of a page visible
+ xAtOffset = Math.min(contentWidth - horizontalScrollOffset, xAtOffset + horizontalIncrement);
+ scrollHorizontalBar(xAtOffset - clientAreaWidth);
+ }
+ if (line < topIndex) {
+ setVerticalScrollOffset(line * verticalIncrement, true);
+ }
+ else
+ if (line > getBottomIndex()) {
+ setVerticalScrollOffset((line - getBottomIndex()) * verticalIncrement + verticalScrollOffset, true);
+ }
+}
+/**
+ * Scrolls the selection into view.
+ * <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();
+ showOffset(selection.x);
+ showOffset(selection.y);
+}
+/**
+ * Returns the width of the specified text. Expand 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 width of the text with tabs expanded to tab stops or 0 if the
+ * length is beyond the text length.
+ */
+int textWidth(String line, int lineIndex, int length, GC gc) {
+ StyleRange[] styles = null;
+ int lineOffset = content.getOffsetAtLine(lineIndex);
+ StyledTextEvent event = getLineStyleData(lineOffset, line);
+ StyledTextBidi bidi = null;
+
+ if (event != null) {
+ styles = filterLineStyles(event.styles);
+ }
+ if (isBidi()) {
+ int[] boldStyles = getBoldRanges(styles, lineOffset, line.length());
+ bidi = new StyledTextBidi(gc, tabWidth, line, boldStyles, boldFont, getStyleOffsets (line, lineOffset));
+ }
+ return textWidth(line, lineOffset, 0, length, styles, 0, gc, bidi);
+}
+/**
+ * Returns the width of the specified text. Expand tabs to tab stops using
+ * the widget tab width.
+ * <p>
+ *
+ * @param text text to be measured.
+ * @param lineOffset offset of the first character in the line.
+ * @param startOffset offset of the character to start measuring and
+ * expand tabs.
+ * @param length number of characters to measure. Tabs are counted
+ * as one character in this parameter.
+ * @param styles line styles
+ * @param startXOffset x position of "startOffset" in "text". Used for
+ * calculating tab stops
+ * @param gc GC to use for measuring text
+ * @param bidi the bidi object to use for measuring text in bidi locales.
+ * null when not in bidi mode.
+ * @return width of the text with tabs expanded to tab stops or 0 if the
+ * startOffset or length is outside the specified text.
+ */
+int textWidth(String text, int lineOffset, int startOffset, int length, StyleRange[] lineStyles, int startXOffset, GC gc, StyledTextBidi bidi) {
+ int paintX = 0;
+ int endOffset = startOffset + length;
+ int textLength = text.length();
+ FontData fontData = gc.getFont().getFontData()[0];
+
+ if (startOffset < 0 || startOffset >= textLength || startOffset + length > textLength) {
+ return paintX;
+ }
+ if (bidi != null) {
+ paintX = bidi.getCaretPosition(endOffset) - startXOffset;
+ }
+ else {
+ for (int i = startOffset; i < endOffset; i++) {
+ int tabIndex = text.indexOf(TAB, i);
+ // is tab not present or past the rendering range?
+ if (tabIndex == -1 || tabIndex > endOffset) {
+ tabIndex = endOffset;
+ }
+ if (tabIndex != i) {
+ String tabSegment = text.substring(i, tabIndex);
+ if (lineStyles != null) {
+ paintX = styledTextWidth(tabSegment, lineOffset + i, lineStyles, paintX, 0, gc, fontData);
+ }
+ else {
+ setLineFont(gc, fontData, SWT.NORMAL);
+ paintX += gc.stringExtent(tabSegment).x;
+ }
+ if (tabIndex != endOffset && tabWidth > 0) {
+ paintX += tabWidth;
+ paintX -= (startXOffset + paintX) % tabWidth;
+ }
+ i = tabIndex;
+ }
+ else
+ if (tabWidth > 0) {
+ paintX += tabWidth;
+ paintX -= (startXOffset + paintX) % tabWidth;
+ }
+ }
+ }
+ return paintX;
+}
+/**
+ * Measures the text as rendered at the specified location. Expand tabs to tab stops using
+ * the widget tab width.
+ * <p>
+ *
+ * @param text text to draw
+ * @param textStartOffset offset of the first character in text relative
+ * to the first character in the document
+ * @param lineStyles styles of the line
+ * @param paintX x location to start drawing at
+ * @param paintY y location to draw at
+ * @param gc GC to draw on
+ * @param fontData the font data of the font currently set in gc
+ * @return x location where drawing stopped or 0 if the startOffset or
+ * length is outside the specified text.
+ */
+int styledTextWidth(String text, int textStartOffset, StyleRange[] lineStyles, int paintX, int paintY, GC gc, FontData fontData) {
+ String textSegment;
+ int textLength = text.length();
+ int textIndex = 0;
+ for (int styleIndex = 0; styleIndex < lineStyles.length; styleIndex++) {
+ StyleRange style = lineStyles[styleIndex];
+ int textEnd;
+ int styleSegmentStart = style.start - textStartOffset;
+ if (styleSegmentStart + style.length < 0) {
+ continue;
+ }
+ if (styleSegmentStart >= textLength) {
+ break;
+ }
+ // is there a style for the current string position?
+ if (textIndex < styleSegmentStart) {
+ setLineFont(gc, fontData, SWT.NORMAL);
+ textSegment = text.substring(textIndex, styleSegmentStart);
+ paintX += gc.stringExtent(textSegment).x;
+ textIndex = styleSegmentStart;
+ }
+ textEnd = Math.min(textLength, styleSegmentStart + style.length);
+ setLineFont(gc, fontData, style.fontStyle);
+ textSegment = text.substring(textIndex, textEnd);
+ paintX += gc.stringExtent(textSegment).x;
+ textIndex = textEnd;
+ }
+ // is there unmeasured and unstyled text?
+ if (textIndex < textLength) {
+ setLineFont(gc, fontData, SWT.NORMAL);
+ textSegment = text.substring(textIndex, textLength);
+ paintX += gc.stringExtent(textSegment).x;
+ }
+ return paintX;
+}
+/**
+ * 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);
+ // 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);
+ // always update the caret location. fixes 1G8FODP
+ setCaretLocation();
+ }
+}
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextBidi.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextBidi.java
new file mode 100644
index 0000000000..3b76d81b11
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextBidi.java
@@ -0,0 +1,719 @@
+package org.eclipse.swt.custom;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.internal.BidiUtil;
+import org.eclipse.swt.internal.Converter;
+import org.eclipse.swt.widgets.Control;
+import java.io.*;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/*
+ */
+class StyledTextBidi {
+ GC gc;
+ int tabWidth;
+ int[] renderPositions;
+ int[] order;
+ int[] dx;
+ byte[] classBuffer;
+ byte[] glyphBuffer;
+
+ 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];
+
+ if (visualEnd < visualStart) {
+ visualStart = visualEnd;
+ }
+ return visualStart;
+ }
+ int getVisualEnd() {
+ int visualStart = order[logicalStart];
+ int visualEnd = order[logicalEnd];
+
+ 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();
+ }
+ }
+
+public StyledTextBidi(GC gc, int tabWidth, String text, int[] boldRanges, Font boldFont, int [] offsets) {
+ int length = text.length();
+
+ setGC(gc);
+ setTabWidth(tabWidth);
+ renderPositions = new int[length];
+ order = new int[length];
+ dx = new int[length];
+ classBuffer = new byte[length];
+ if (text.length() == 0) {
+ glyphBuffer = new byte[0];
+ }
+ else {
+ glyphBuffer = BidiUtil.getRenderInfo(gc, text, order, classBuffer, dx, 0, offsets);
+ if (boldRanges != null) {
+ Font normalFont = gc.getFont();
+ gc.setFont(boldFont);
+ for (int i = 0; i < boldRanges.length; i += 2) {
+ int rangeStart = boldRanges[i];
+ int rangeLength = boldRanges[i + 1];
+ prepareBoldText(text, rangeStart, rangeLength);
+ }
+ gc.setFont(normalFont);
+ }
+ adjustTabStops(text);
+ calculateRenderPositions();
+ }
+}
+public static void addLanguageListener (int hwnd, Runnable runnable) {
+ BidiUtil.addLanguageListener(hwnd, runnable);
+}
+void adjustTabStops(String text) {
+ int tabIndex = text.indexOf('\t', 0);
+ int logicalIndex = 0;
+ int x = StyledText.xInset;
+
+ while (tabIndex != -1) {
+ for (; logicalIndex < tabIndex; logicalIndex++) {
+ x += dx[order[logicalIndex]];
+ }
+ int tabStop = x + tabWidth;
+ tabStop -= tabStop % tabWidth;
+ dx[order[tabIndex]] = tabStop - x;
+ tabIndex = text.indexOf('\t', tabIndex + 1);
+ }
+}
+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];
+ }
+}
+/**
+ *
+ * @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
+ */
+public int drawBidiText(int logicalStart, int length, int xOffset, int yOffset) {
+ Enumeration directionRuns = getDirectionRuns(logicalStart, length).elements();
+ int endOffset = logicalStart + length;
+ int stopX;
+
+ if (endOffset > getTextLength()) {
+ return StyledText.xInset;
+ }
+ 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);
+ }
+ // between R2L and L2R direction segment?
+ if (endOffset < order.length && isRightToLeft(endOffset) == false && isRightToLeft(endOffset - 1)) {
+ // continue drawing at start of L2R segment
+ stopX = renderPositions[endOffset];
+ }
+ else
+ if (isRightToLeft(endOffset - 1)) {
+ // we rendered a R2L segment
+ stopX = renderPositions[order[endOffset - 1]];
+ }
+ else {
+ int visualEnd = order[endOffset - 1];
+ // we rendered a L2R segment
+ stopX = renderPositions[visualEnd] + dx[visualEnd];
+ }
+ return stopX;
+}
+/**
+ *
+ */
+void drawGlyphs(int visualStart, int length, int x, int y) {
+ byte[] renderBuffer = new byte[length * 2];
+ int[] renderDx = new int[length];
+
+ if (length == 0) {
+ return;
+ }
+ System.arraycopy(glyphBuffer, visualStart * 2, renderBuffer, 0, length * 2);
+ // copy the distance values for the desired rendering range
+ System.arraycopy(dx, visualStart, renderDx, 0, length);
+ BidiUtil.drawGlyphs(gc, renderBuffer, renderDx, x, y);
+}
+public boolean equals(Object object) {
+ StyledTextBidi test;
+ if (object == this) return true;
+ if (object instanceof StyledTextBidi) test = (StyledTextBidi) object;
+ else return false;
+
+ int[] intArray1 = test.order;
+ int[] intArray2 = this.order;
+ if (intArray1.length != intArray2.length) return false;
+ for (int i=0; i<intArray1.length; i++) {
+ if (intArray1[i] != intArray2[i]) return false;
+ }
+ intArray1 = test.dx;
+ intArray2 = this.dx;
+ if (intArray1.length != intArray2.length) return false;
+ for (int i=0; i<intArray1.length; i++) {
+ if (intArray1[i] != intArray2[i]) return false;
+ }
+ intArray1 = test.renderPositions;
+ intArray2 = this.renderPositions;
+ if (intArray1.length != intArray2.length) return false;
+ for (int i=0; i<intArray1.length; i++) {
+ if (intArray1[i] != intArray2[i]) return false;
+ }
+ byte[] byteArray1 = test.classBuffer;
+ byte[] byteArray2 = this.classBuffer;
+ if (byteArray1.length != byteArray2.length) return false;
+ for (int i=0; i<byteArray1.length; i++) {
+ if (byteArray1[i] != byteArray2[i]) return false;
+ }
+ byteArray1 = test.glyphBuffer;
+ byteArray2 = this.glyphBuffer;
+ if (byteArray1.length != byteArray2.length) return false;
+ for (int i=0; i<byteArray1.length; i++) {
+ if (byteArray1[i] != byteArray2[i]) return false;
+ }
+ return true;
+}
+public void fillBackground(int logicalStart, int length, int xOffset, int yOffset, int height) {
+ Enumeration directionRuns = getDirectionRuns(logicalStart, length).elements();
+
+ if (logicalStart + length > getTextLength()) {
+ return;
+ }
+ while (directionRuns.hasMoreElements()) {
+ DirectionRun run = (DirectionRun) directionRuns.nextElement();
+ int visualStart = run.getVisualStart();
+ int visualEnd = run.getVisualEnd();
+ int startX = run.getRenderStartX();
+ gc.fillRectangle(xOffset + startX, yOffset, run.getRenderStopX() - startX, height);
+ }
+}
+public int[] getCaretOffsetAndDirectionAtX(int x) {
+ int lineLength = getTextLength();
+ int low = -1;
+ int high = lineLength;
+ int offset;
+
+ 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 index visually clicked character
+ int visualIndex = 0;
+ int visualX = renderPositions[visualIndex];
+ while ((x > visualX) && (visualIndex < renderPositions.length)) {
+ visualIndex++;
+ if (visualIndex < renderPositions.length) visualX = renderPositions[visualIndex];
+ }
+ visualIndex = Math.max(0,visualIndex-1);
+ // figure out if the character was clicked on the right or left
+ int halfway = renderPositions[visualIndex] + (dx[visualIndex] / 2);
+ boolean visualLeft = (x <= halfway);
+ int direction;
+ offset = getLogicalOffset(visualIndex);
+
+ // handle visual beginning
+ if (visualIndex == 0) {
+ if (isRightToLeft(offset)) {
+ if (visualLeft) {
+ offset = getLigatureOffset(offset);
+ offset = offset + 1;
+ direction = ST.COLUMN_NEXT;
+ }
+ else {
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ }
+ else {
+ if (visualLeft) {
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ else {
+ offset = offset + 1;
+ direction = ST.COLUMN_NEXT;
+ }
+ }
+ return new int[] {offset, direction};
+ }
+
+ // handle visual end
+ if (visualIndex == renderPositions.length - 1) {
+ if (isRightToLeft(offset)) {
+ if (visualLeft) {
+ offset = getLigatureOffset(offset);
+ offset = offset + 1;
+ direction = ST.COLUMN_NEXT;
+ }
+ else {
+ offset = offset;
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ }
+ else {
+ if (visualLeft) {
+ offset = offset;
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ else {
+ offset = offset + 1;
+ direction = ST.COLUMN_NEXT;
+ }
+ }
+ return new int[] {offset, direction};
+ }
+
+ if (isRightToLeft(offset)) {
+ if (visualLeft) {
+ offset = getLigatureOffset(offset);
+ offset = offset + 1;
+ direction = ST.COLUMN_NEXT;
+ }
+ else {
+ offset = offset;
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ }
+ else {
+ if (visualLeft) {
+ offset = offset;
+ direction = ST.COLUMN_PREVIOUS;
+ }
+ else {
+ offset = offset + 1;
+ direction = ST.COLUMN_NEXT;
+ }
+ }
+ return new int[] {offset, direction};
+}
+public int getCaretPosition(int logicalOffset) {
+ int caretX;
+
+ if (getTextLength() == 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
+ if (logicalOffset > 0 && isRightToLeft(logicalOffset) != isRightToLeft(logicalOffset - 1)) {
+ // logically first character of different direction segment?
+ // go behind last character of previous direction segment
+ int visualOffset = order[logicalOffset - 1];
+ if (isRightToLeft(logicalOffset - 1)) {
+ caretX = renderPositions[visualOffset];
+ }
+ else {
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ }
+ else
+ // at R2L character or beginning of line?
+ if (isRightToLeft(logicalOffset) || logicalOffset == 0) {
+ int visualOffset = order[logicalOffset];
+
+
+ if (isRightToLeft(logicalOffset)) {
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ else {
+ caretX = renderPositions[visualOffset];
+ }
+ }
+ else {
+ // L2R segment
+ caretX = renderPositions[order[logicalOffset]];
+ }
+ return caretX;
+}
+public int getCaretPosition(int logicalOffset, int direction) {
+ // moving to character at logicalOffset
+ if (getTextLength() == 0) {
+ return StyledText.xInset;
+ }
+ int caretX;
+ // 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];
+ }
+ setKeyboardLanguage(logicalOffset);
+ return caretX;
+ }
+
+ // at beginning of line?
+ if (logicalOffset == 0) {
+ int visualOffset = order[logicalOffset];
+ if (isRightToLeft(logicalOffset)) {
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ else {
+ caretX = renderPositions[visualOffset];
+ }
+ setKeyboardLanguage(logicalOffset);
+ return caretX;
+ }
+
+ if (direction == ST.COLUMN_NEXT) {
+ if (isRightToLeft(logicalOffset) != isRightToLeft(logicalOffset - 1)) {
+ // moving between segments
+ if (isRightToLeft(logicalOffset - 1)) {
+ // moving from RtoL to LtoR
+ int visualOffset = order[logicalOffset-1];
+ caretX = renderPositions[visualOffset];
+ }
+ else {
+ // moving from LtoR to RtoL
+ int visualOffset = order[logicalOffset-1];
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ setKeyboardLanguage(logicalOffset-1);
+ return caretX;
+ }
+ }
+
+ if (direction == ST.COLUMN_PREVIOUS) {
+ if (isRightToLeft(logicalOffset) != isRightToLeft(logicalOffset - 1)) {
+ // moving between segments
+ if (isRightToLeft(logicalOffset - 1)) {
+ // moving from LtoR to RtoL
+ int visualOffset = order[logicalOffset];
+ caretX = renderPositions[visualOffset];
+ }
+ else {
+ // moving from RtoL to LtoR
+ int visualOffset = order[logicalOffset];
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ setKeyboardLanguage(logicalOffset);
+ return caretX;
+ }
+ }
+
+ if (isRightToLeft(logicalOffset)) {
+ int visualOffset = order[logicalOffset];
+ caretX = renderPositions[visualOffset] + dx[visualOffset];
+ }
+ else {
+ caretX = renderPositions[order[logicalOffset]];
+ }
+ setKeyboardLanguage(logicalOffset);
+ return caretX;
+}
+
+Vector getDirectionRuns(int logicalStart, int length) {
+ Vector directionRuns = new Vector();
+ int logicalEnd = logicalStart + length - 1;
+ int segmentLogicalStart = logicalStart;
+ int segmentLogicalEnd = segmentLogicalStart;
+
+ if (logicalEnd < getTextLength()) {
+ while (segmentLogicalEnd <= logicalEnd) {
+ int segType = classBuffer[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 && segType == classBuffer[segmentLogicalEnd + 1]) {
+ segmentLogicalEnd++;
+ }
+ directionRuns.addElement(new DirectionRun(segmentLogicalStart, segmentLogicalEnd));
+ segmentLogicalStart = ++segmentLogicalEnd;
+ }
+ }
+ return directionRuns;
+}
+/**
+ * Answer SWT.LEFT or SWT.RIGHT.
+ */
+public static int getKeyboardLanguageDirection() {
+ int language = BidiUtil.getKeyboardLanguage();
+ if (language == BidiUtil.KEYBOARD_HEBREW) {
+ return SWT.RIGHT;
+ }
+ if (language == BidiUtil.KEYBOARD_ARABIC) {
+ return SWT.RIGHT;
+ }
+ return SWT.LEFT;
+}
+int getLigatureOffset(int offset) {
+ // should call BidiText to see if font has ligatures...
+ int newOffset = offset;
+ int i = offset + 1;
+ while (i<order.length && (order[i] == order[offset])) {
+ newOffset = i;
+ i++;
+ }
+ return newOffset;
+}
+int getLogicalOffset(int visualOffset) {
+ int logicalOffset = 0;
+
+ while (logicalOffset < order.length && order[logicalOffset] != visualOffset) {
+ logicalOffset++;
+ }
+ return logicalOffset;
+}
+public int getOffsetAtX(int x) {
+ int lineLength = getTextLength();
+ int low = -1;
+ int high = lineLength;
+
+ if (lineLength == 0) {
+ return 0;
+ }
+ if (x > renderPositions[renderPositions.length - 1] + dx[dx.length - 1]) {
+ return lineLength;
+ }
+ while (high - low > 1) {
+ int offset = (high + low) / 2;
+ int visualX = renderPositions[offset];
+
+
+ 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 getLogicalOffset(high);
+}
+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;
+}
+int getTextLength() {
+ return order.length;
+}
+public int getTextWidth() {
+ int width = 0;
+
+ if (getTextLength() > 0) {
+ width = renderPositions[renderPositions.length - 1] + dx[dx.length - 1];
+ }
+ return width;
+}
+/**
+ *
+ */
+public boolean isRightToLeft(int logicalIndex) {
+ // for rendering, caret positioning, consider numbers as LtoR
+ boolean isRightToLeft = false;
+
+ if (logicalIndex < classBuffer.length) {
+ isRightToLeft = (classBuffer[logicalIndex] == BidiUtil.CLASS_ARABIC) ||
+ (classBuffer[logicalIndex] == BidiUtil.CLASS_HEBREW);
+ }
+ return isRightToLeft;
+}
+/**
+ *
+ */
+public boolean isRightToLeftMode(int logicalIndex) {
+ // for keyboard positioning, consider numbers as RtoL
+ boolean isRightToLeft = false;
+
+ if (logicalIndex < classBuffer.length) {
+ isRightToLeft = (classBuffer[logicalIndex] == BidiUtil.CLASS_ARABIC) ||
+ (classBuffer[logicalIndex] == BidiUtil.CLASS_HEBREW) ||
+ (classBuffer[logicalIndex] == BidiUtil.CLASS_LOCALNUMBER);
+ }
+ return isRightToLeft;
+}/**
+ *
+ */
+void prepareBoldText(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
+ if (logicalStart != 0
+ && !Character.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
+ && !Character.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);
+ // 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;
+ }
+}
+public void redrawRange(Control parent, int logicalStart, int length, int xOffset, int yOffset, int height) {
+ Enumeration directionRuns = getDirectionRuns(logicalStart, length).elements();
+
+ if (logicalStart + length > getTextLength()) {
+ return;
+ }
+ while (directionRuns.hasMoreElements()) {
+ DirectionRun run = (DirectionRun) directionRuns.nextElement();
+ int visualStart = run.getVisualStart();
+ int visualEnd = run.getVisualEnd();
+ int startX = run.getRenderStartX();
+
+ parent.redraw(xOffset + startX, yOffset, run.getRenderStopX() - startX, height, true);
+ }
+}
+public static void removeLanguageListener (int hwnd) {
+ BidiUtil.removeLanguageListener(hwnd);
+}
+void setGC(GC gc) {
+ this.gc = gc;
+}
+/**
+ *
+ */
+public void setKeyboardLanguage(int logicalIndex) {
+ int language = BidiUtil.KEYBOARD_LATIN;
+
+ if (logicalIndex >= classBuffer.length) {
+ return;
+ }
+ if (isRightToLeftMode(logicalIndex)) {
+ String codePage = System.getProperty("file.encoding").toUpperCase();
+ if ("CP1255".equals(codePage)) {
+ language = BidiUtil.KEYBOARD_HEBREW;
+ }
+ else
+ if ("CP1256".equals(codePage)) {
+ language = BidiUtil.KEYBOARD_ARABIC;
+ }
+ }
+ BidiUtil.setKeyboardLanguage(language);
+}
+void setTabWidth(int tabWidth) {
+ this.tabWidth = tabWidth;
+}
+/**
+ * Should change output to conform with other SWT toString output (i.e., Class {value1, value2})
+ */
+public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("\n");
+ buf.append("Render Positions: ");
+ buf.append("\n");
+ for (int i=0; i<renderPositions.length; i++) {
+ buf.append(renderPositions[i]);
+ buf.append(" ");
+ }
+ buf.append("\n");
+ buf.append("Order: ");
+ buf.append("\n");
+ for (int i=0; i<order.length; i++) {
+ buf.append(order[i]);
+ buf.append(" ");
+ }
+ buf.append("\n");
+ buf.append("DX: ");
+ buf.append("\n");
+ for (int i=0; i<dx.length; i++) {
+ buf.append(dx[i]);
+ buf.append(" ");
+ }
+ buf.append("\n");
+ buf.append("Class: ");
+ buf.append("\n");
+ for (int i=0; i<classBuffer.length; i++) {
+ buf.append(classBuffer[i]);
+ buf.append(" ");
+ }
+ buf.append("\n");
+ buf.append("Glyph Buffer: ");
+ buf.append("\n");
+ for (int i=0; i<glyphBuffer.length; i++) {
+ buf.append(glyphBuffer[i]);
+ buf.append(" ");
+ }
+/* buf.append("\n");
+ buf.append("Glyphs: ");
+ buf.append("\n");
+ buf.append(getGlyphs());
+ buf.append("\n");
+*/
+ return buf.toString();
+}
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextContent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextContent.java
new file mode 100644
index 0000000000..cd4995f94d
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextContent.java
@@ -0,0 +1,187 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.events.*;
+
+public interface StyledTextContent {
+
+/**
+ * Called by StyledText to add itself as an Observer to content changes.
+ * Implementors should send a TextChangingEvent when changes to the content
+ * are about to occur and a TextChangedEvent when the changes did occur.
+ * The widget only updates the screen properly when it receives both events.
+ * <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 character offset without delimiters.
+ * <p>
+ *
+ * @param offset offset of the line to return. Does not include delimiters
+ * of preceeding lines. Offset 0 is the first character of the document.
+ * @return the line text without delimiters
+ */
+public String getLine(int offset);
+
+/**
+ * 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 before the
+ * content is changed and a <code>TextChangedEvent</code> has to be sent
+ * 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
+ */
+public void replaceTextRange(int start, int replaceLength, String text);
+
+/**
+ * Set text to "text".
+ * Implementors have to send a <code>TextChangedEvent</code> to the
+ * TextChangeListeners that were added using <code>addTextChangeListener</code>.
+ * <p>
+ *
+ * @param text the new text
+ */
+public void setText(String text);
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextEvent.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextEvent.java
new file mode 100644
index 0000000000..f67b669139
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextEvent.java
@@ -0,0 +1,30 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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 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_j2se/org/eclipse/swt/custom/StyledTextListener.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextListener.java
new file mode 100644
index 0000000000..2808634a45
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextListener.java
@@ -0,0 +1,65 @@
+package org.eclipse.swt.custom;
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import java.util.*;
+
+class StyledTextListener extends TypedListener {
+/**
+ */
+StyledTextListener(EventListener 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.LineGetStyle:
+ LineStyleEvent lineStyleEvent = new LineStyleEvent((StyledTextEvent) e);
+ ((LineStyleListener) eventListener).lineGetStyle(lineStyleEvent);
+ ((StyledTextEvent) e).styles = lineStyleEvent.styles;
+ break;
+
+ case StyledText.LineGetBackground:
+ LineBackgroundEvent lineBgEvent = new LineBackgroundEvent((StyledTextEvent) e);
+ ((LineBackgroundListener) eventListener).lineGetBackground(lineBgEvent);
+ ((StyledTextEvent) e).lineBackground = lineBgEvent.lineBackground;
+ break;
+
+ case StyledText.VerifyKey:
+ VerifyEvent verifyEvent = new VerifyEvent(e);
+ ((VerifyKeyListener) eventListener).verifyKey(verifyEvent);
+ e.doit = verifyEvent.doit;
+ break;
+
+ case StyledText.TextChanging:
+ TextChangingEvent textChangingEvent = new TextChangingEvent((StyledTextContent) e.data, (StyledTextEvent) e);
+ ((TextChangeListener) eventListener).textChanging(textChangingEvent);
+ break;
+
+ case StyledText.TextChanged:
+ textChangedEvent = new TextChangedEvent((StyledTextContent) e.data);
+ ((TextChangeListener) eventListener).textChanged(textChangedEvent);
+ 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_j2se/org/eclipse/swt/custom/StyledTextPrinter.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextPrinter.java
new file mode 100644
index 0000000000..bc90308868
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/StyledTextPrinter.java
@@ -0,0 +1,352 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.custom.*;
+import org.eclipse.swt.printing.*;
+
+class StyledTextPrinter implements Runnable {
+ class RTFState {
+ int fontStyle;
+ int foreground;
+ int background;
+ }
+ Vector savedState = new Vector();
+
+ Printer printer;
+ GC gc;
+ String rtf;
+ StringBuffer wordBuffer;
+ int index, end;
+ String tabs = "";
+ int tabWidth = 0;
+ int lineHeight = 0;
+ int leftMargin, rightMargin, topMargin, bottomMargin;
+ int x, y;
+
+ /* We can optimize for fonts because we know styledText only has one font.
+ * As soon as we know the font name and point size, we will create and store
+ * fonts for the following styles: normal, bold, italic, and bold italic.
+ */
+ Font fontTable[][] = new Font[1][4];
+ boolean creatingFontTable = false;
+ String fontName;
+ int fontSize;
+ int currentFontStyle = SWT.NORMAL;
+ int currentFont = -1;
+ int defaultFont = -1;
+
+ Vector colorTable = new Vector();
+ boolean creatingColorTable = false;
+ int red, green, blue;
+ int currentForeground = -1;
+ int currentBackground = -1;
+
+ static void print(StyledText styledText) {
+ Printer printer = new Printer();
+ new StyledTextPrinter(styledText, printer).run();
+ printer.dispose();
+ }
+
+ StyledTextPrinter(StyledText styledText, Printer printer) {
+ this.printer = printer;
+
+ /* Create a buffer for computing tab width. */
+ int tabSize = styledText.getTabs();
+ StringBuffer tabBuffer = new StringBuffer(tabSize);
+ for (int i = 0; i < tabSize; i++) tabBuffer.append(' ');
+ tabs = tabBuffer.toString();
+
+ /* Get RTF from the StyledText.*/
+ rtf = styledText.getRtf();
+ }
+
+ public void run() {
+ if (printer.startJob("Printing")) {
+ Rectangle clientArea = printer.getClientArea();
+ Rectangle trim = printer.computeTrim(0, 0, 0, 0);
+ Point dpi = printer.getDPI();
+ leftMargin = dpi.x + trim.x; // one inch from left side of paper
+ rightMargin = clientArea.width - dpi.x + trim.x + trim.width; // one inch from right side of paper
+ topMargin = dpi.y + trim.y; // one inch from top edge of paper
+ bottomMargin = clientArea.height - dpi.y + trim.y + trim.height; // one inch from bottom edge of paper
+
+ /* Create a printer GC and print the RTF to it. */
+ gc = new GC(printer);
+ x = leftMargin;
+ y = topMargin;
+ printer.startPage();
+ end = rtf.length();
+ index = 0;
+ wordBuffer = new StringBuffer();
+ while (index < end) {
+ char c = rtf.charAt(index);
+ index++;
+ switch (c) {
+ case '\\':
+ printWordBuffer();
+ parseControlWord();
+ break;
+ case '{':
+ printWordBuffer();
+ saveState();
+ break;
+ case '}':
+ printWordBuffer();
+ restoreState();
+ break;
+ case 0x0a:
+ case 0x0d:
+ printWordBuffer();
+ break;
+ default:
+ parseChar(c);
+ }
+ }
+ if (y + lineHeight <= bottomMargin) {
+ printer.endPage();
+ }
+ printer.endJob();
+
+ /* Cleanup */
+ gc.dispose();
+ for (int i = 0; i < 4; i++) {
+ fontTable[currentFont][i].dispose();
+ }
+ for (int i = 0; i < colorTable.size(); i++) {
+ ((Color)colorTable.elementAt(i)).dispose();
+ }
+ }
+ }
+
+ void parseControlWord() {
+ if (index >= end) return;
+ char c = rtf.charAt(index);
+ index++;
+ if (!Character.isLetter(c)) {
+ handleControlSymbol(c);
+ return;
+ }
+ StringBuffer controlWord = new StringBuffer();
+ controlWord.append(c);
+ while (index < end) {
+ c = rtf.charAt(index);
+ index++;
+ if (!Character.isLetter(c)) break;
+ controlWord.append(c);
+ }
+ boolean isNegative = false;
+ if (c == '-') {
+ isNegative = true;
+ c = rtf.charAt(index);
+ index++;
+ }
+ boolean hasParameter = false;
+ StringBuffer paramBuffer = new StringBuffer();
+ int parameter = 0;
+ if (Character.isDigit(c)) {
+ hasParameter = true;
+ paramBuffer.append(c);
+ while (index < end) {
+ c = rtf.charAt(index);
+ index++;
+ if (!Character.isDigit(c)) break;
+ paramBuffer.append(c);
+ }
+ try {
+ parameter = Integer.valueOf(paramBuffer.toString()).intValue();
+ } catch (NumberFormatException e) {}
+ if (isNegative) parameter = -parameter;
+ }
+ if (c != ' ') index--;
+ if (hasParameter) {
+ handleControlWord(controlWord.toString(), parameter);
+ } else {
+ handleControlWord(controlWord.toString());
+ }
+ }
+
+ void parseChar(char c) {
+ if (c == 0) return;
+ if (c == ';') {
+ if (creatingFontTable) {
+ fontName = wordBuffer.toString();
+ wordBuffer = new StringBuffer();
+ creatingFontTable = false;
+ return;
+ }
+ if (creatingColorTable) {
+ colorTable.addElement(new Color(printer, red, green, blue));
+ red = green = blue = 0;
+ return;
+ }
+ }
+ if (c != '\t') {
+ wordBuffer.append(c);
+ }
+ if (!Character.isLetterOrDigit(c) && !creatingFontTable) {
+ printWordBuffer();
+ if (c == '\t') {
+ x += tabWidth;
+ }
+ }
+ }
+
+ void printWordBuffer() {
+ if (wordBuffer.length() > 0) {
+ String word = wordBuffer.toString();
+ int wordWidth = gc.stringExtent(word).x;
+ if (x + wordWidth > rightMargin) {
+ /* word doesn't fit on current line, so wrap */
+ newline();
+ }
+ gc.drawString(word, x, y, true);
+ x += wordWidth;
+ wordBuffer = new StringBuffer();
+ }
+ }
+
+ void handleControlSymbol(char c) {
+ switch (c) {
+ case '\\':
+ case '{':
+ case '}':
+ parseChar(c);
+ }
+ }
+
+ void handleControlWord(String controlWord) {
+ if (controlWord.equals("par")) newline();
+ else if (controlWord.equals("b")) setFontStyle(currentFontStyle | SWT.BOLD);
+ else if (controlWord.equals("i")) setFontStyle(currentFontStyle | SWT.ITALIC);
+ else if (controlWord.equals("fnil")) setFont(defaultFont);
+ else if (controlWord.equals("fonttbl")) createFontTable();
+ else if (controlWord.equals("colortbl")) createColorTable();
+ }
+
+ void handleControlWord(String controlWord, int parameter) {
+ if (controlWord.equals("highlight")) setBackground(parameter);
+ else if (controlWord.equals("cf")) setForeground(parameter);
+ else if (controlWord.equals("b")) setFontStyle(currentFontStyle & ~SWT.BOLD);
+ else if (controlWord.equals("i")) setFontStyle(currentFontStyle & ~SWT.ITALIC);
+ else if (controlWord.equals("f")) setFont(parameter);
+ else if (controlWord.equals("fs")) setFontSize(parameter);
+ else if (controlWord.equals("red")) red = parameter;
+ else if (controlWord.equals("green")) green = parameter;
+ else if (controlWord.equals("blue")) blue = parameter;
+ else if (controlWord.equals("deff")) setDefaultFont(parameter);
+ }
+
+ void setDefaultFont(int number) {
+ defaultFont = number;
+ }
+
+ void setFont(int number) {
+ currentFont = number;
+ }
+
+ void createFontTable() {
+ creatingFontTable = true;
+ currentFont = 0;
+ }
+
+ void setFontSize(int size) {
+ fontSize = size / 2;
+ createFonts();
+ }
+
+ void createFonts() {
+ if (fontName != null && fontSize != -1) {
+ // currentFont must already be set
+ fontTable[currentFont][0] = new Font(printer, fontName, fontSize, SWT.NORMAL);
+ fontTable[currentFont][1] = new Font(printer, fontName, fontSize, SWT.BOLD);
+ fontTable[currentFont][2] = new Font(printer, fontName, fontSize, SWT.ITALIC);
+ fontTable[currentFont][3] = new Font(printer, fontName, fontSize, SWT.BOLD | SWT.ITALIC);
+ setFontStyle(SWT.NORMAL);
+ }
+ }
+
+ void setFontStyle(int style) {
+ // currentFont must already be set
+ Font font;
+ if ((style & SWT.BOLD) != 0) {
+ if ((style & SWT.ITALIC) != 0) {
+ font = fontTable[currentFont][3];
+ } else {
+ font = fontTable[currentFont][1];
+ }
+ } else if ((style & SWT.ITALIC) != 0) {
+ font = fontTable[currentFont][2];
+ } else {
+ font = fontTable[currentFont][0];
+ }
+ gc.setFont(font);
+ tabWidth = gc.stringExtent(tabs).x;
+ lineHeight = gc.getFontMetrics().getHeight();
+ currentFontStyle = style;
+ }
+
+ void createColorTable() {
+ creatingColorTable = true;
+ red = green = blue = 0;
+ }
+
+ void setForeground(int color) {
+ if (color != currentForeground) {
+ // colors must already be in table
+ gc.setForeground((Color)colorTable.elementAt(color));
+ currentForeground = color;
+ }
+ }
+
+ void setBackground(int color) {
+ if (color != currentBackground) {
+ // colors must already be in table
+ gc.setBackground((Color)colorTable.elementAt(color));
+ currentBackground = color;
+ }
+ }
+
+ void newline() {
+ x = leftMargin;
+ y += lineHeight;
+ if (y + lineHeight > bottomMargin) {
+ printer.endPage();
+ if (index + 1 < end) {
+ y = topMargin;
+ printer.startPage();
+ }
+ }
+ }
+
+ void saveState() {
+ RTFState state = new RTFState();
+ state.fontStyle = currentFontStyle;
+ state.foreground = currentForeground;
+ state.background = currentBackground;
+ savedState.addElement(state);
+ }
+
+ void restoreState() {
+ if (savedState.isEmpty()) return;
+ if (creatingColorTable) {
+ setForeground(0);
+ setBackground(1);
+ creatingColorTable = false;
+ }
+ RTFState state = (RTFState)savedState.remove(savedState.size() - 1);
+ setFontStyle(state.fontStyle);
+ if (state.foreground != -1) setForeground(state.foreground);
+ if (state.background != -1) setBackground(state.background);
+ }
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableEditor.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableEditor.java
new file mode 100644
index 0000000000..1fdca24770
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableEditor.java
@@ -0,0 +1,179 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+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);
+ }
+
+ 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;
+}
+public void setColumn(int column) {
+
+ if (this.column > -1 && this.column < table.getColumnCount()){
+ 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);
+}
+/**
+* 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 setItem (TableItem item) {
+ this.item = item;
+}
+public void setEditor (Control editor) {
+ TableItem item = null;
+ if (table.getItemCount() > 0) {
+ item = table.getItem(0);
+ }
+ this.setEditor(editor, item, 0);
+}
+
+/**
+* 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);
+ super.setEditor(editor);
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableTree.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableTree.java
new file mode 100644
index 0000000000..ae8005ab4c
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/eclipse/swt/custom/TableTree.java
@@ -0,0 +1,708 @@
+package org.eclipse.swt.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved
+ */
+
+import java.util.Enumeration;
+import java.util.Vector;
+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>
+ */
+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];
+
+/**
+ * Creates a new instance of the widget.
+ *
+ * @param parent a composite widget
+ * @param style the bitwise OR'ing of widget styles
+ */
+public TableTree(Composite parent, int style) {
+ super(parent, SWT.NONE);
+ table = new Table(this, style);
+ setBackground(table.getBackground());
+ setForeground(table.getForeground());
+ setFont(table.getFont());
+ table.addListener(SWT.MouseDown, new Listener() {
+ public void handleEvent(Event e) {
+ onMouseDown(e);
+ }
+ });
+ table.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event e) {
+ onSelection(e);
+ }
+ });
+ table.addListener(SWT.DefaultSelection, new Listener() {
+ public void handleEvent(Event e) {
+ onSelection(e);
+ }
+ });
+
+ addListener(SWT.Dispose, new Listener() {
+ public void handleEvent(Event e) {
+ onDispose();
+ }
+ });
+ addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ onResize();
+ }
+ });
+ addListener(SWT.FocusIn, new Listener() {
+ public void handleEvent(Event e) {
+ onFocusIn();
+ }
+ });
+}
+
+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 receive selection events.
+ * <p>
+ *
+ * @param listener the selection 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 addSelectionListener(SelectionListener listener) {
+ 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) {
+ if (listener == null) throw new SWTError (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Expand, typedListener);
+ addListener (SWT.Collapse, typedListener);
+}
+
+/**
+ * Computes the preferred size of the widget.
+ * <p>
+ * Calculate the preferred size of the widget based
+ * on the current contents. The hint arguments allow
+ * a specific client area width and/or height to be
+ * requested. The hints may be honored depending on
+ * the platform and the layout.
+ *
+ * @param wHint the width hint (can be SWT.DEFAULT)
+ * @param hHint the height hint (can be SWT.DEFAULT)
+ * @return a point containing the preferred size of the widget including trim
+ *
+ * @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 Point computeSize (int wHint, int hHint) {
+ return table.computeSize (wHint, hHint, true);
+}
+
+/**
+ * Computes the widget trim.
+ * <p>
+ * Trim is widget specific and may include scroll
+ * bars and menu bar in addition to other trimmings
+ * that are outside of the widget's client area.
+ *
+ * @param x the x location of the client area
+ * @param y the y location of the client area
+ * @param width the width of the client area
+ * @param height the height of the client area
+ * @return a rectangle containing the trim of 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>
+ * </ul>
+ */
+public Rectangle computeTrim (int x, int y, int width, int height) {
+ 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 () {
+ table.deselectAll();
+}
+
+/* Expand upward from the specified leaf item. */
+void expandItem (TableTreeItem item) {
+ if (item == null || item.getExpanded()) return;
+ expandItem(item.parentItem);
+ item.setExpanded(true);
+ Event event = new Event();
+ event.item = item;
+ notifyListeners(SWT.Expand, event);
+}
+
+/**
+ * Gets the number of items.
+ * <p>
+ * @return the number of items in the widget
+ */
+public int getItemCount () {
+ 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 () {
+ return table.getItemHeight();
+}
+
+/**
+ * Gets the items.
+ * <p>
+ * @return the items in the widget
+ *
+ */
+public TableTreeItem [] getItems () {
+ 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 () {
+ TableItem[] selection = table.getSelection();
+ TableTreeItem [] result = new TableTreeItem[selection.length];
+ for (int i = 0; i < selection.length; i++){
+ result[i] = (TableTreeItem) selection[i].getData();
+ }
+ 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 () {
+ return table.getSelectionCount();
+}
+
+/**
+ * Returns the underlying Table control.
+ *
+ * @return the underlying Table control
+ */
+public Table getTable () {
+ 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) {
+ for (int i = 0; i < items.length; i++) {
+ if (item == items[i]) return i;
+ }
+ return -1;
+}
+
+void onDispose() {
+ 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 () {
+ Rectangle area = getClientArea();
+ table.setBounds(0, 0, area.width, area.height);
+}
+
+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);
+}
+public TableTreeItem getItem(Point point) {
+ 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 () {
+ table.setFocus();
+}
+
+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();
+ 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 () {
+ 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) {
+ 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) {
+ 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 () {
+ table.selectAll();
+}
+
+/**
+ * Sets the widget background color.
+ * <p>
+ * When new color is null, the background reverts
+ * to the default system color for the widget.
+ *
+ * @param color the new color (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 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();
+ }
+}
+
+/**
+ * Sets the enabled state.
+ * <p>
+ * A disabled widget is typically not selectable from
+ * the user interface and draws with an inactive or
+ * grayed look.
+ *
+ * @param enabled the new enabled 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 setEnabled (boolean enabled) {
+ super.setEnabled(enabled);
+ table.setEnabled(enabled);
+}
+
+/**
+ * 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) {
+ super.setFont(font);
+ table.setFont(font);
+}
+
+/**
+ * Gets the widget foreground color.
+ * <p>
+ * @return the widget foreground color
+ *
+ * @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 setForeground (Color color) {
+ super.setForeground(color);
+ table.setForeground(color);
+}
+
+/**
+ * Sets the pop up menu.
+ * <p>
+ * Every control has an optional pop up menu that is
+ * displayed when the user requests a popup menu for
+ * the control. The sequence of key strokes/button
+ * presses/button releases that is used to request
+ * a pop up menu is platform specific.
+ *
+ * @param menu the new pop up menu
+ *
+ * @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_MENU_NOT_POP_UP when the menu is not a POP_UP</li>
+ * <li>ERROR_NO_COMMON_PARENT when the menu is not in the same widget tree</li>
+ * </ul>
+ */
+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) {
+ 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);
+}
+
+/**
+ * Sets the tool tip text.
+ * <p>
+ * @param string the new tool tip text (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 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) {
+ 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 () {
+ table.showSelection();
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common_j2se/org/ec