| 1 |
/******************************************************************************* |
|---|
| 2 |
* Copyright (c) 2000, 2008 IBM Corporation and others. |
|---|
| 3 |
* All rights reserved. This program and the accompanying materials |
|---|
| 4 |
* are made available under the terms of the Eclipse Public License v1.0 |
|---|
| 5 |
* which accompanies this distribution, and is available at |
|---|
| 6 |
* http://www.eclipse.org/legal/epl-v10.html |
|---|
| 7 |
* |
|---|
| 8 |
* Contributors: |
|---|
| 9 |
* IBM Corporation - initial API and implementation |
|---|
| 10 |
* Port to the D programming language: |
|---|
| 11 |
* Frank Benoit <benoit@tionex.de> |
|---|
| 12 |
*******************************************************************************/ |
|---|
| 13 |
module dwt.widgets.Link; |
|---|
| 14 |
|
|---|
| 15 |
import dwt.DWT; |
|---|
| 16 |
import dwt.DWTException; |
|---|
| 17 |
import dwt.accessibility.ACC; |
|---|
| 18 |
import dwt.accessibility.Accessible; |
|---|
| 19 |
import dwt.accessibility.AccessibleAdapter; |
|---|
| 20 |
import dwt.accessibility.AccessibleControlAdapter; |
|---|
| 21 |
import dwt.accessibility.AccessibleControlEvent; |
|---|
| 22 |
import dwt.accessibility.AccessibleEvent; |
|---|
| 23 |
import dwt.events.SelectionEvent; |
|---|
| 24 |
import dwt.events.SelectionListener; |
|---|
| 25 |
import dwt.graphics.Color; |
|---|
| 26 |
import dwt.graphics.Font; |
|---|
| 27 |
import dwt.graphics.GC; |
|---|
| 28 |
import dwt.graphics.GCData; |
|---|
| 29 |
import dwt.graphics.Point; |
|---|
| 30 |
import dwt.graphics.RGB; |
|---|
| 31 |
import dwt.graphics.Rectangle; |
|---|
| 32 |
import dwt.graphics.TextLayout; |
|---|
| 33 |
import dwt.graphics.TextStyle; |
|---|
| 34 |
import dwt.internal.win32.OS; |
|---|
| 35 |
|
|---|
| 36 |
import dwt.widgets.Control; |
|---|
| 37 |
import dwt.widgets.Composite; |
|---|
| 38 |
import dwt.widgets.TypedListener; |
|---|
| 39 |
import dwt.widgets.Event; |
|---|
| 40 |
|
|---|
| 41 |
import dwt.dwthelper.utils; |
|---|
| 42 |
|
|---|
| 43 |
static import tango.text.Text; |
|---|
| 44 |
alias tango.text.Text.Text!(char) StringBuffer; |
|---|
| 45 |
|
|---|
| 46 |
/** |
|---|
| 47 |
* Instances of this class represent a selectable |
|---|
| 48 |
* user interface object that displays a text with |
|---|
| 49 |
* links. |
|---|
| 50 |
* <p> |
|---|
| 51 |
* <dl> |
|---|
| 52 |
* <dt><b>Styles:</b></dt> |
|---|
| 53 |
* <dd>(none)</dd> |
|---|
| 54 |
* <dt><b>Events:</b></dt> |
|---|
| 55 |
* <dd>Selection</dd> |
|---|
| 56 |
* </dl> |
|---|
| 57 |
* <p> |
|---|
| 58 |
* IMPORTANT: This class is <em>not</em> intended to be subclassed. |
|---|
| 59 |
* </p> |
|---|
| 60 |
* |
|---|
| 61 |
* @see <a href="http://www.eclipse.org/swt/snippets/#link">Link snippets</a> |
|---|
| 62 |
* @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: ControlExample</a> |
|---|
| 63 |
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> |
|---|
| 64 |
* |
|---|
| 65 |
* @since 3.1 |
|---|
| 66 |
*/ |
|---|
| 67 |
public class Link : Control { |
|---|
| 68 |
|
|---|
| 69 |
alias Control.computeSize computeSize; |
|---|
| 70 |
alias Control.windowProc windowProc; |
|---|
| 71 |
|
|---|
| 72 |
String text; |
|---|
| 73 |
TextLayout layout; |
|---|
| 74 |
Color linkColor, disabledColor; |
|---|
| 75 |
Point [] offsets; |
|---|
| 76 |
Point selection; |
|---|
| 77 |
String [] ids; |
|---|
| 78 |
int [] mnemonics; |
|---|
| 79 |
int focusIndex, mouseDownIndex; |
|---|
| 80 |
HFONT font; |
|---|
| 81 |
static /+const+/ RGB LINK_FOREGROUND; |
|---|
| 82 |
static /+const+/ WNDPROC LinkProc; |
|---|
| 83 |
static const TCHAR[] LinkClass = OS.WC_LINK; |
|---|
| 84 |
|
|---|
| 85 |
private static bool static_this_completed = false; |
|---|
| 86 |
private static void static_this() { |
|---|
| 87 |
if( static_this_completed ){ |
|---|
| 88 |
return; |
|---|
| 89 |
} |
|---|
| 90 |
synchronized { |
|---|
| 91 |
if( static_this_completed ){ |
|---|
| 92 |
return; |
|---|
| 93 |
} |
|---|
| 94 |
LINK_FOREGROUND = new RGB (0, 51, 153); |
|---|
| 95 |
if (OS.COMCTL32_MAJOR >= 6) { |
|---|
| 96 |
WNDCLASS lpWndClass; |
|---|
| 97 |
OS.GetClassInfo (null, LinkClass.ptr, &lpWndClass); |
|---|
| 98 |
LinkProc = lpWndClass.lpfnWndProc; |
|---|
| 99 |
/* |
|---|
| 100 |
* Feature in Windows. The SysLink window class |
|---|
| 101 |
* does not include CS_DBLCLKS. This means that these |
|---|
| 102 |
* controls will not get double click messages such as |
|---|
| 103 |
* WM_LBUTTONDBLCLK. The fix is to register a new |
|---|
| 104 |
* window class with CS_DBLCLKS. |
|---|
| 105 |
* |
|---|
| 106 |
* NOTE: Screen readers look for the exact class name |
|---|
| 107 |
* of the control in order to provide the correct kind |
|---|
| 108 |
* of assistance. Therefore, it is critical that the |
|---|
| 109 |
* new window class have the same name. It is possible |
|---|
| 110 |
* to register a local window class with the same name |
|---|
| 111 |
* as a global class. Since bits that affect the class |
|---|
| 112 |
* are being changed, it is possible that other native |
|---|
| 113 |
* code, other than DWT, could create a control with |
|---|
| 114 |
* this class name, and fail unexpectedly. |
|---|
| 115 |
*/ |
|---|
| 116 |
auto hInstance = OS.GetModuleHandle (null); |
|---|
| 117 |
auto hHeap = OS.GetProcessHeap (); |
|---|
| 118 |
lpWndClass.hInstance = hInstance; |
|---|
| 119 |
lpWndClass.style &= ~OS.CS_GLOBALCLASS; |
|---|
| 120 |
lpWndClass.style |= OS.CS_DBLCLKS; |
|---|
| 121 |
int byteCount = LinkClass.length * TCHAR.sizeof; |
|---|
| 122 |
auto lpszClassName = cast(TCHAR*) OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount); |
|---|
| 123 |
OS.MoveMemory (lpszClassName, LinkClass.ptr, byteCount); |
|---|
| 124 |
lpWndClass.lpszClassName = lpszClassName; |
|---|
| 125 |
OS.RegisterClass (&lpWndClass); |
|---|
| 126 |
OS.HeapFree (hHeap, 0, lpszClassName); |
|---|
| 127 |
} else { |
|---|
| 128 |
LinkProc = null; |
|---|
| 129 |
} |
|---|
| 130 |
static_this_completed = true; |
|---|
| 131 |
} |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
/** |
|---|
| 135 |
* Constructs a new instance of this class given its parent |
|---|
| 136 |
* and a style value describing its behavior and appearance. |
|---|
| 137 |
* <p> |
|---|
| 138 |
* The style value is either one of the style constants defined in |
|---|
| 139 |
* class <code>DWT</code> which is applicable to instances of this |
|---|
| 140 |
* class, or must be built by <em>bitwise OR</em>'ing together |
|---|
| 141 |
* (that is, using the <code>int</code> "|" operator) two or more |
|---|
| 142 |
* of those <code>DWT</code> style constants. The class description |
|---|
| 143 |
* lists the style constants that are applicable to the class. |
|---|
| 144 |
* Style bits are also inherited from superclasses. |
|---|
| 145 |
* </p> |
|---|
| 146 |
* |
|---|
| 147 |
* @param parent a composite control which will be the parent of the new instance (cannot be null) |
|---|
| 148 |
* @param style the style of control to construct |
|---|
| 149 |
* |
|---|
| 150 |
* @exception IllegalArgumentException <ul> |
|---|
| 151 |
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
|---|
| 152 |
* </ul> |
|---|
| 153 |
* @exception DWTException <ul> |
|---|
| 154 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
|---|
| 155 |
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> |
|---|
| 156 |
* </ul> |
|---|
| 157 |
* |
|---|
| 158 |
* @see Widget#checkSubclass |
|---|
| 159 |
* @see Widget#getStyle |
|---|
| 160 |
*/ |
|---|
| 161 |
public this (Composite parent, int style) { |
|---|
| 162 |
static_this(); |
|---|
| 163 |
super (parent, style); |
|---|
| 164 |
} |
|---|
| 165 |
|
|---|
| 166 |
/** |
|---|
| 167 |
* Adds the listener to the collection of listeners who will |
|---|
| 168 |
* be notified when the control is selected by the user, by sending |
|---|
| 169 |
* it one of the messages defined in the <code>SelectionListener</code> |
|---|
| 170 |
* interface. |
|---|
| 171 |
* <p> |
|---|
| 172 |
* <code>widgetSelected</code> is called when the control is selected by the user. |
|---|
| 173 |
* <code>widgetDefaultSelected</code> is not called. |
|---|
| 174 |
* </p> |
|---|
| 175 |
* |
|---|
| 176 |
* @param listener the listener which should be notified |
|---|
| 177 |
* |
|---|
| 178 |
* @exception IllegalArgumentException <ul> |
|---|
| 179 |
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
|---|
| 180 |
* </ul> |
|---|
| 181 |
* @exception DWTException <ul> |
|---|
| 182 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 183 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 184 |
* </ul> |
|---|
| 185 |
* |
|---|
| 186 |
* @see SelectionListener |
|---|
| 187 |
* @see #removeSelectionListener |
|---|
| 188 |
* @see SelectionEvent |
|---|
| 189 |
*/ |
|---|
| 190 |
public void addSelectionListener (SelectionListener listener) { |
|---|
| 191 |
checkWidget (); |
|---|
| 192 |
if (listener is null) error (DWT.ERROR_NULL_ARGUMENT); |
|---|
| 193 |
TypedListener typedListener = new TypedListener (listener); |
|---|
| 194 |
addListener (DWT.Selection, typedListener); |
|---|
| 195 |
addListener (DWT.DefaultSelection, typedListener); |
|---|
| 196 |
} |
|---|
| 197 |
|
|---|
| 198 |
override int callWindowProc (HWND hwnd, int msg, int wParam, int lParam) { |
|---|
| 199 |
if (handle is null) return 0; |
|---|
| 200 |
if (LinkProc !is null) { |
|---|
| 201 |
/* |
|---|
| 202 |
* Feature in Windows. By convention, native Windows controls |
|---|
| 203 |
* check for a non-NULL wParam, assume that it is an HDC and |
|---|
| 204 |
* paint using that device. The SysLink control does not. |
|---|
| 205 |
* The fix is to check for an HDC and use WM_PRINTCLIENT. |
|---|
| 206 |
*/ |
|---|
| 207 |
switch (msg) { |
|---|
| 208 |
case OS.WM_PAINT: |
|---|
| 209 |
if (wParam !is 0) { |
|---|
| 210 |
OS.SendMessage (hwnd, OS.WM_PRINTCLIENT, wParam, 0); |
|---|
| 211 |
return 0; |
|---|
| 212 |
} |
|---|
| 213 |
break; |
|---|
| 214 |
default: |
|---|
| 215 |
} |
|---|
| 216 |
return OS.CallWindowProc (LinkProc, hwnd, msg, wParam, lParam); |
|---|
| 217 |
} |
|---|
| 218 |
return OS.DefWindowProc (hwnd, msg, wParam, lParam); |
|---|
| 219 |
} |
|---|
| 220 |
|
|---|
| 221 |
override public Point computeSize (int wHint, int hHint, bool changed) { |
|---|
| 222 |
checkWidget (); |
|---|
| 223 |
if (wHint !is DWT.DEFAULT && wHint < 0) wHint = 0; |
|---|
| 224 |
if (hHint !is DWT.DEFAULT && hHint < 0) hHint = 0; |
|---|
| 225 |
int width, height; |
|---|
| 226 |
if (OS.COMCTL32_MAJOR >= 6) { |
|---|
| 227 |
auto hDC = OS.GetDC (handle); |
|---|
| 228 |
auto newFont = cast(HFONT) OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); |
|---|
| 229 |
auto oldFont = OS.SelectObject (hDC, newFont); |
|---|
| 230 |
if (text.length > 0) { |
|---|
| 231 |
TCHAR[] buffer = StrToTCHARs (getCodePage (), parse (text)); |
|---|
| 232 |
RECT rect; |
|---|
| 233 |
int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX; |
|---|
| 234 |
if (wHint !is DWT.DEFAULT) { |
|---|
| 235 |
flags |= OS.DT_WORDBREAK; |
|---|
| 236 |
rect.right = wHint; |
|---|
| 237 |
} |
|---|
| 238 |
OS.DrawText (hDC, buffer.ptr, buffer.length, &rect, flags); |
|---|
| 239 |
width = rect.right - rect.left; |
|---|
| 240 |
height = rect.bottom; |
|---|
| 241 |
} else { |
|---|
| 242 |
TEXTMETRIC lptm; |
|---|
| 243 |
OS.GetTextMetrics (hDC, &lptm); |
|---|
| 244 |
width = 0; |
|---|
| 245 |
height = lptm.tmHeight; |
|---|
| 246 |
} |
|---|
| 247 |
if (newFont !is null) OS.SelectObject (hDC, oldFont); |
|---|
| 248 |
OS.ReleaseDC (handle, hDC); |
|---|
| 249 |
} else { |
|---|
| 250 |
int layoutWidth = layout.getWidth (); |
|---|
| 251 |
//TEMPORARY CODE |
|---|
| 252 |
if (wHint is 0) { |
|---|
| 253 |
layout.setWidth (1); |
|---|
| 254 |
Rectangle rect = layout.getBounds (); |
|---|
| 255 |
width = 0; |
|---|
| 256 |
height = rect.height; |
|---|
| 257 |
} else { |
|---|
| 258 |
layout.setWidth (wHint); |
|---|
| 259 |
Rectangle rect = layout.getBounds (); |
|---|
| 260 |
width = rect.width; |
|---|
| 261 |
height = rect.height; |
|---|
| 262 |
} |
|---|
| 263 |
layout.setWidth (layoutWidth); |
|---|
| 264 |
} |
|---|
| 265 |
if (wHint !is DWT.DEFAULT) width = wHint; |
|---|
| 266 |
if (hHint !is DWT.DEFAULT) height = hHint; |
|---|
| 267 |
int border = getBorderWidth (); |
|---|
| 268 |
width += border * 2; |
|---|
| 269 |
height += border * 2; |
|---|
| 270 |
return new Point (width, height); |
|---|
| 271 |
} |
|---|
| 272 |
|
|---|
| 273 |
override void createHandle () { |
|---|
| 274 |
super.createHandle (); |
|---|
| 275 |
state |= THEME_BACKGROUND; |
|---|
| 276 |
if (OS.COMCTL32_MAJOR < 6) { |
|---|
| 277 |
layout = new TextLayout (display); |
|---|
| 278 |
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) { |
|---|
| 279 |
linkColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_HOTLIGHT)); |
|---|
| 280 |
} else { |
|---|
| 281 |
linkColor = new Color (display, LINK_FOREGROUND); |
|---|
| 282 |
} |
|---|
| 283 |
disabledColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_GRAYTEXT)); |
|---|
| 284 |
offsets = new Point [0]; |
|---|
| 285 |
ids = new String [0]; |
|---|
| 286 |
mnemonics = new int [0]; |
|---|
| 287 |
selection = new Point (-1, -1); |
|---|
| 288 |
focusIndex = mouseDownIndex = -1; |
|---|
| 289 |
} |
|---|
| 290 |
} |
|---|
| 291 |
|
|---|
| 292 |
override void createWidget () { |
|---|
| 293 |
super.createWidget (); |
|---|
| 294 |
text = ""; |
|---|
| 295 |
if (OS.COMCTL32_MAJOR < 6) { |
|---|
| 296 |
if ((style & DWT.MIRRORED) !is 0) { |
|---|
| 297 |
layout.setOrientation (DWT.RIGHT_TO_LEFT); |
|---|
| 298 |
} |
|---|
| 299 |
initAccessible (); |
|---|
| 300 |
} |
|---|
| 301 |
} |
|---|
| 302 |
|
|---|
| 303 |
void drawWidget (GC gc, RECT* rect) { |
|---|
| 304 |
drawBackground (gc.handle, rect); |
|---|
| 305 |
int selStart = selection.x; |
|---|
| 306 |
int selEnd = selection.y; |
|---|
| 307 |
if (selStart > selEnd) { |
|---|
| 308 |
selStart = selection.y; |
|---|
| 309 |
selEnd = selection.x; |
|---|
| 310 |
} |
|---|
| 311 |
// temporary code to disable text selection |
|---|
| 312 |
selStart = selEnd = -1; |
|---|
| 313 |
if (!OS.IsWindowEnabled (handle)) gc.setForeground (disabledColor); |
|---|
| 314 |
layout.draw (gc, 0, 0, selStart, selEnd, null, null); |
|---|
| 315 |
if (hasFocus () && focusIndex !is -1) { |
|---|
| 316 |
Rectangle [] rects = getRectangles (focusIndex); |
|---|
| 317 |
for (int i = 0; i < rects.length; i++) { |
|---|
| 318 |
Rectangle rectangle = rects [i]; |
|---|
| 319 |
gc.drawFocus (rectangle.x, rectangle.y, rectangle.width, rectangle.height); |
|---|
| 320 |
} |
|---|
| 321 |
} |
|---|
| 322 |
if (hooks (DWT.Paint) || filters (DWT.Paint)) { |
|---|
| 323 |
Event event = new Event (); |
|---|
| 324 |
event.gc = gc; |
|---|
| 325 |
event.x = rect.left; |
|---|
| 326 |
event.y = rect.top; |
|---|
| 327 |
event.width = rect.right - rect.left; |
|---|
| 328 |
event.height = rect.bottom - rect.top; |
|---|
| 329 |
sendEvent (DWT.Paint, event); |
|---|
| 330 |
event.gc = null; |
|---|
| 331 |
} |
|---|
| 332 |
} |
|---|
| 333 |
|
|---|
| 334 |
override void enableWidget (bool enabled) { |
|---|
| 335 |
if (OS.COMCTL32_MAJOR >= 6) { |
|---|
| 336 |
LITEM item; |
|---|
| 337 |
item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE; |
|---|
| 338 |
item.stateMask = OS.LIS_ENABLED; |
|---|
| 339 |
item.state = enabled ? OS.LIS_ENABLED : 0; |
|---|
| 340 |
while (OS.SendMessage (handle, OS.LM_SETITEM, 0, &item) !is 0) { |
|---|
| 341 |
item.iLink++; |
|---|
| 342 |
} |
|---|
| 343 |
} else { |
|---|
| 344 |
TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null); |
|---|
| 345 |
linkStyle.underline = true; |
|---|
| 346 |
for (int i = 0; i < offsets.length; i++) { |
|---|
| 347 |
Point point = offsets [i]; |
|---|
| 348 |
layout.setStyle (linkStyle, point.x, point.y); |
|---|
| 349 |
} |
|---|
| 350 |
redraw (); |
|---|
| 351 |
} |
|---|
| 352 |
/* |
|---|
| 353 |
* Feature in Windows. For some reason, setting |
|---|
| 354 |
* LIS_ENABLED state using LM_SETITEM causes the |
|---|
| 355 |
* SysLink to become enabled. To be specific, |
|---|
| 356 |
* calling IsWindowEnabled() returns true. The |
|---|
| 357 |
* fix is disable the SysLink after LM_SETITEM. |
|---|
| 358 |
*/ |
|---|
| 359 |
super.enableWidget (enabled); |
|---|
| 360 |
} |
|---|
| 361 |
|
|---|
| 362 |
void initAccessible () { |
|---|
| 363 |
Accessible accessible = getAccessible (); |
|---|
| 364 |
accessible.addAccessibleListener (new class() AccessibleAdapter { |
|---|
| 365 |
public void getName (AccessibleEvent e) { |
|---|
| 366 |
e.result = parse (text); |
|---|
| 367 |
} |
|---|
| 368 |
}); |
|---|
| 369 |
|
|---|
| 370 |
accessible.addAccessibleControlListener (new class() AccessibleControlAdapter { |
|---|
| 371 |
public void getChildAtPoint (AccessibleControlEvent e) { |
|---|
| 372 |
e.childID = ACC.CHILDID_SELF; |
|---|
| 373 |
} |
|---|
| 374 |
|
|---|
| 375 |
public void getLocation (AccessibleControlEvent e) { |
|---|
| 376 |
Rectangle rect = display.map (getParent (), null, getBounds ()); |
|---|
| 377 |
e.x = rect.x; |
|---|
| 378 |
e.y = rect.y; |
|---|
| 379 |
e.width = rect.width; |
|---|
| 380 |
e.height = rect.height; |
|---|
| 381 |
} |
|---|
| 382 |
|
|---|
| 383 |
public void getChildCount (AccessibleControlEvent e) { |
|---|
| 384 |
e.detail = 0; |
|---|
| 385 |
} |
|---|
| 386 |
|
|---|
| 387 |
public void getRole (AccessibleControlEvent e) { |
|---|
| 388 |
e.detail = ACC.ROLE_LINK; |
|---|
| 389 |
} |
|---|
| 390 |
|
|---|
| 391 |
public void getState (AccessibleControlEvent e) { |
|---|
| 392 |
e.detail = ACC.STATE_FOCUSABLE; |
|---|
| 393 |
if (hasFocus ()) e.detail |= ACC.STATE_FOCUSED; |
|---|
| 394 |
} |
|---|
| 395 |
|
|---|
| 396 |
public void getDefaultAction (AccessibleControlEvent e) { |
|---|
| 397 |
e.result = DWT.getMessage ("SWT_Press"); //$NON-NLS-1$ |
|---|
| 398 |
} |
|---|
| 399 |
|
|---|
| 400 |
public void getSelection (AccessibleControlEvent e) { |
|---|
| 401 |
if (hasFocus ()) e.childID = ACC.CHILDID_SELF; |
|---|
| 402 |
} |
|---|
| 403 |
|
|---|
| 404 |
public void getFocus (AccessibleControlEvent e) { |
|---|
| 405 |
if (hasFocus ()) e.childID = ACC.CHILDID_SELF; |
|---|
| 406 |
} |
|---|
| 407 |
}); |
|---|
| 408 |
} |
|---|
| 409 |
|
|---|
| 410 |
override String getNameText () { |
|---|
| 411 |
return getText (); |
|---|
| 412 |
} |
|---|
| 413 |
|
|---|
| 414 |
Rectangle [] getRectangles (int linkIndex) { |
|---|
| 415 |
int lineCount = layout.getLineCount (); |
|---|
| 416 |
Rectangle [] rects = new Rectangle [lineCount]; |
|---|
| 417 |
int [] lineOffsets = layout.getLineOffsets (); |
|---|
| 418 |
Point point = offsets [linkIndex]; |
|---|
| 419 |
int lineStart = 1; |
|---|
| 420 |
while (point.x > lineOffsets [lineStart]) lineStart++; |
|---|
| 421 |
int lineEnd = 1; |
|---|
| 422 |
while (point.y > lineOffsets [lineEnd]) lineEnd++; |
|---|
| 423 |
int index = 0; |
|---|
| 424 |
if (lineStart is lineEnd) { |
|---|
| 425 |
rects [index++] = layout.getBounds (point.x, point.y); |
|---|
| 426 |
} else { |
|---|
| 427 |
rects [index++] = layout.getBounds (point.x, lineOffsets [lineStart]-1); |
|---|
| 428 |
rects [index++] = layout.getBounds (lineOffsets [lineEnd-1], point.y); |
|---|
| 429 |
if (lineEnd - lineStart > 1) { |
|---|
| 430 |
for (int i = lineStart; i < lineEnd - 1; i++) { |
|---|
| 431 |
rects [index++] = layout.getLineBounds (i); |
|---|
| 432 |
} |
|---|
| 433 |
} |
|---|
| 434 |
} |
|---|
| 435 |
if (rects.length !is index) { |
|---|
| 436 |
Rectangle [] tmp = new Rectangle [index]; |
|---|
| 437 |
System.arraycopy (rects, 0, tmp, 0, index); |
|---|
| 438 |
rects = tmp; |
|---|
| 439 |
} |
|---|
| 440 |
return rects; |
|---|
| 441 |
} |
|---|
| 442 |
|
|---|
| 443 |
/** |
|---|
| 444 |
* Returns the receiver's text, which will be an empty |
|---|
| 445 |
* string if it has never been set. |
|---|
| 446 |
* |
|---|
| 447 |
* @return the receiver's text |
|---|
| 448 |
* |
|---|
| 449 |
* @exception DWTException <ul> |
|---|
| 450 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 451 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 452 |
* </ul> |
|---|
| 453 |
*/ |
|---|
| 454 |
public String getText () { |
|---|
| 455 |
checkWidget (); |
|---|
| 456 |
return text; |
|---|
| 457 |
} |
|---|
| 458 |
|
|---|
| 459 |
String parse (String string) { |
|---|
| 460 |
int length_ = string.length; |
|---|
| 461 |
offsets = new Point [length_ / 4]; |
|---|
| 462 |
ids = new String [length_ / 4]; |
|---|
| 463 |
mnemonics = new int [length_ / 4 + 1]; |
|---|
| 464 |
StringBuffer result = new StringBuffer (); |
|---|
| 465 |
char [] buffer = new char [length_]; |
|---|
| 466 |
string.getChars (0, string.length, buffer, 0); |
|---|
| 467 |
int index = 0, state = 0, linkIndex = 0; |
|---|
| 468 |
int start = 0, tagStart = 0, linkStart = 0, endtagStart = 0, refStart = 0; |
|---|
| 469 |
|
|---|
| 470 |
while (index < length_) { |
|---|
| 471 |
|
|---|
| 472 |
// instead Character.toLower |
|---|
| 473 |
// only needed for the control symbols, which are always ascii |
|---|
| 474 |
char c = buffer[index]; |
|---|
| 475 |
if( c >= 'A' && c <= 'Z' ){ |
|---|
| 476 |
c -= ( 'A' - 'a' ); |
|---|
| 477 |
} |
|---|
| 478 |
|
|---|
| 479 |
// only simple ascii whitespace needed |
|---|
| 480 |
bool isWhitespace(char w ){ |
|---|
| 481 |
switch(w){ |
|---|
| 482 |
case ' ': return true; |
|---|
| 483 |
default : return false; |
|---|
| 484 |
} |
|---|
| 485 |
} |
|---|
| 486 |
|
|---|
| 487 |
switch (state) { |
|---|
| 488 |
case 0: |
|---|
| 489 |
if (c is '<') { |
|---|
| 490 |
tagStart = index; |
|---|
| 491 |
state++; |
|---|
| 492 |
} |
|---|
| 493 |
break; |
|---|
| 494 |
case 1: |
|---|
| 495 |
if (c is 'a') state++; |
|---|
| 496 |
break; |
|---|
| 497 |
case 2: |
|---|
| 498 |
switch (c) { |
|---|
| 499 |
case 'h': |
|---|
| 500 |
state = 7; |
|---|
| 501 |
break; |
|---|
| 502 |
case '>': |
|---|
| 503 |
linkStart = index + 1; |
|---|
| 504 |
state++; |
|---|
| 505 |
break; |
|---|
| 506 |
default: |
|---|
| 507 |
if (isWhitespace(c)) break; |
|---|
| 508 |
else state = 13; |
|---|
| 509 |
} |
|---|
| 510 |
break; |
|---|
| 511 |
case 3: |
|---|
| 512 |
if (c is '<') { |
|---|
| 513 |
endtagStart = index; |
|---|
| 514 |
state++; |
|---|
| 515 |
} |
|---|
| 516 |
break; |
|---|
| 517 |
case 4: |
|---|
| 518 |
state = c is '/' ? state + 1 : 3; |
|---|
| 519 |
break; |
|---|
| 520 |
case 5: |
|---|
| 521 |
state = c is 'a' ? state + 1 : 3; |
|---|
| 522 |
break; |
|---|
| 523 |
case 6: |
|---|
| 524 |
if (c is '>') { |
|---|
| 525 |
mnemonics [linkIndex] = parseMnemonics (buffer, start, tagStart, result); |
|---|
| 526 |
int offset = result.length; |
|---|
| 527 |
parseMnemonics (buffer, linkStart, endtagStart, result); |
|---|
| 528 |
offsets [linkIndex] = new Point (offset, result.length - 1); |
|---|
| 529 |
if (ids [linkIndex] is null) { |
|---|
| 530 |
ids [linkIndex] = buffer[ linkStart .. endtagStart ].dup; |
|---|
| 531 |
} |
|---|
| 532 |
linkIndex++; |
|---|
| 533 |
start = tagStart = linkStart = endtagStart = refStart = index + 1; |
|---|
| 534 |
state = 0; |
|---|
| 535 |
} else { |
|---|
| 536 |
state = 3; |
|---|
| 537 |
} |
|---|
| 538 |
break; |
|---|
| 539 |
case 7: |
|---|
| 540 |
state = c is 'r' ? state + 1 : 0; |
|---|
| 541 |
break; |
|---|
| 542 |
case 8: |
|---|
| 543 |
state = c is 'e' ? state + 1 : 0; |
|---|
| 544 |
break; |
|---|
| 545 |
case 9: |
|---|
| 546 |
state = c is 'f' ? state + 1 : 0; |
|---|
| 547 |
break; |
|---|
| 548 |
case 10: |
|---|
| 549 |
state = c is '=' ? state + 1 : 0; |
|---|
| 550 |
break; |
|---|
| 551 |
case 11: |
|---|
| 552 |
if (c is '"') { |
|---|
| 553 |
state++ |
|---|