comparison org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/widgets/Link.d @ 0:6dd524f61e62

add dwt win and basic java stuff
author Frank Benoit <benoit@tionex.de>
date Mon, 02 Mar 2009 14:44:16 +0100
parents
children 4c0057e71936
comparison
equal deleted inserted replaced
-1:000000000000 0:6dd524f61e62
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 org.eclipse.swt.widgets.Link;
14
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.SWTException;
17 import org.eclipse.swt.accessibility.ACC;
18 import org.eclipse.swt.accessibility.Accessible;
19 import org.eclipse.swt.accessibility.AccessibleAdapter;
20 import org.eclipse.swt.accessibility.AccessibleControlAdapter;
21 import org.eclipse.swt.accessibility.AccessibleControlEvent;
22 import org.eclipse.swt.accessibility.AccessibleEvent;
23 import org.eclipse.swt.events.SelectionEvent;
24 import org.eclipse.swt.events.SelectionListener;
25 import org.eclipse.swt.graphics.Color;
26 import org.eclipse.swt.graphics.Font;
27 import org.eclipse.swt.graphics.GC;
28 import org.eclipse.swt.graphics.GCData;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.RGB;
31 import org.eclipse.swt.graphics.Rectangle;
32 import org.eclipse.swt.graphics.TextLayout;
33 import org.eclipse.swt.graphics.TextStyle;
34 import org.eclipse.swt.internal.win32.OS;
35
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.TypedListener;
39 import org.eclipse.swt.widgets.Event;
40
41 import java.lang.all;
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">SWT 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 SWT, 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>SWT</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>SWT</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 SWTException <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 SWTException <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 (SWT.ERROR_NULL_ARGUMENT);
193 TypedListener typedListener = new TypedListener (listener);
194 addListener (SWT.Selection, typedListener);
195 addListener (SWT.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 SWT.DEFAULT && wHint < 0) wHint = 0;
224 if (hHint !is SWT.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 SWT.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 SWT.DEFAULT) width = wHint;
266 if (hHint !is SWT.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 & SWT.MIRRORED) !is 0) {
297 layout.setOrientation (SWT.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 (SWT.Paint) || filters (SWT.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 (SWT.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 = SWT.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 SWTException <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++;
554 refStart = index + 1;
555 } else {
556 state = 0;
557 }
558 break;
559 case 12:
560 if (c is '"') {
561 ids[linkIndex] = buffer[ refStart .. index ].dup;
562 state = 2;
563 }
564 break;
565 case 13:
566 if (isWhitespace (c)) {
567 state = 0;
568 } else if (c is '='){
569 state++;
570 }
571 break;
572 case 14:
573 state = c is '"' ? state + 1 : 0;
574 break;
575 case 15:
576 if (c is '"') state = 2;
577 break;
578 default:
579 state = 0;
580 break;
581 }
582 index++;
583 }
584 if (start < length_) {
585 int tmp = parseMnemonics (buffer, start, tagStart, result);
586 int mnemonic = parseMnemonics (buffer, Math.max (tagStart, linkStart), length_, result);
587 if (mnemonic is -1) mnemonic = tmp;
588 mnemonics [linkIndex] = mnemonic;
589 } else {
590 mnemonics [linkIndex] = -1;
591 }
592 if (offsets.length !is linkIndex) {
593 Point [] newOffsets = new Point [linkIndex];
594 System.arraycopy (offsets, 0, newOffsets, 0, linkIndex);
595 offsets = newOffsets;
596 String [] newIDs = new String [linkIndex];
597 System.arraycopy (ids, 0, newIDs, 0, linkIndex);
598 ids = newIDs;
599 int [] newMnemonics = new int [linkIndex + 1];
600 System.arraycopy (mnemonics, 0, newMnemonics, 0, linkIndex + 1);
601 mnemonics = newMnemonics;
602 }
603 return result.toString ();
604 }
605
606 int parseMnemonics (char[] buffer, int start, int end, StringBuffer result) {
607 int mnemonic = -1, index = start;
608 while (index < end) {
609 if (buffer [index] is '&') {
610 if (index + 1 < end && buffer [index + 1] is '&') {
611 result.append (buffer [index]);
612 index++;
613 } else {
614 mnemonic = result.length();
615 }
616 } else {
617 result.append (buffer [index]);
618 }
619 index++;
620 }
621 return mnemonic;
622 }
623
624 override void releaseWidget () {
625 super.releaseWidget ();
626 if (layout !is null) layout.dispose ();
627 layout = null;
628 if (linkColor !is null) linkColor.dispose ();
629 linkColor = null;
630 disabledColor = null;
631 offsets = null;
632 ids = null;
633 mnemonics = null;
634 text = null;
635 }
636
637 /**
638 * Removes the listener from the collection of listeners who will
639 * be notified when the control is selected by the user.
640 *
641 * @param listener the listener which should no longer be notified
642 *
643 * @exception IllegalArgumentException <ul>
644 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
645 * </ul>
646 * @exception SWTException <ul>
647 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
648 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
649 * </ul>
650 *
651 * @see SelectionListener
652 * @see #addSelectionListener
653 */
654 public void removeSelectionListener (SelectionListener listener) {
655 checkWidget ();
656 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
657 if (eventTable is null) return;
658 eventTable.unhook (SWT.Selection, listener);
659 eventTable.unhook (SWT.DefaultSelection, listener);
660 }
661
662 /**
663 * Sets the receiver's text.
664 * <p>
665 * The string can contain both regular text and hyperlinks. A hyperlink
666 * is delimited by an anchor tag, &lt;A&gt; and &lt;/A&gt;. Within an
667 * anchor, a single HREF attribute is supported. When a hyperlink is
668 * selected, the text field of the selection event contains either the
669 * text of the hyperlink or the value of its HREF, if one was specified.
670 * In the rare case of identical hyperlinks within the same string, the
671 * HREF tag can be used to distinguish between them. The string may
672 * include the mnemonic character and line delimiters.
673 * </p>
674 *
675 * @param string the new text
676 *
677 * @exception SWTException <ul>
678 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
679 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
680 * </ul>
681 */
682 public void setText (String string) {
683 checkWidget ();
684 // SWT extension: allow null string
685 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
686 if (string.equals (text)) return;
687 text = string;
688 if (OS.COMCTL32_MAJOR >= 6) {
689 bool enabled = cast(bool) OS.IsWindowEnabled (handle);
690 /*
691 * Bug in Windows. For some reason, when SetWindowText()
692 * is used to set the text of a link control to the empty
693 * string, the old text remains. The fix is to set the
694 * text to a space instead.
695 */
696 if (string.length is 0) string = " "; //$NON-NLS-1$
697 TCHAR* buffer = StrToTCHARz (getCodePage (), string);
698 OS.SetWindowText (handle, buffer);
699 parse (text);
700 enableWidget (enabled);
701 } else {
702 layout.setText (parse (text));
703 focusIndex = offsets.length > 0 ? 0 : -1;
704 selection.x = selection.y = -1;
705 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
706 if (offsets.length > 0) {
707 bits |= OS.WS_TABSTOP;
708 } else {
709 bits &= ~OS.WS_TABSTOP;
710 }
711 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
712 bool enabled = cast(bool) OS.IsWindowEnabled (handle);
713 TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
714 linkStyle.underline = true;
715 for (int i = 0; i < offsets.length; i++) {
716 Point point = offsets [i];
717 layout.setStyle (linkStyle, point.x, point.y);
718 }
719 TextStyle mnemonicStyle = new TextStyle (null, null, null);
720 mnemonicStyle.underline = true;
721 for (int i = 0; i < mnemonics.length; i++) {
722 int mnemonic = mnemonics [i];
723 if (mnemonic !is -1) {
724 layout.setStyle (mnemonicStyle, mnemonic, mnemonic);
725 }
726 }
727 redraw ();
728 }
729 }
730
731 override int widgetStyle () {
732 int bits = super.widgetStyle ();
733 return bits | OS.WS_TABSTOP;
734 }
735
736 override String windowClass () {
737 return OS.COMCTL32_MAJOR >= 6 ? TCHARsToStr(LinkClass) : display.windowClass();
738 }
739
740 override int windowProc () {
741 return LinkProc !is null ? cast(int)LinkProc : display.windowProc();
742 }
743
744 override LRESULT WM_CHAR (int wParam, int lParam) {
745 LRESULT result = super.WM_CHAR (wParam, lParam);
746 if (result !is null) return result;
747 if (OS.COMCTL32_MAJOR < 6) {
748 if (focusIndex is -1) return result;
749 switch (wParam) {
750 case ' ':
751 case SWT.CR:
752 Event event = new Event ();
753 event.text = ids [focusIndex];
754 sendEvent (SWT.Selection, event);
755 break;
756 case SWT.TAB:
757 bool next = OS.GetKeyState (OS.VK_SHIFT) >= 0;
758 if (next) {
759 if (focusIndex < offsets.length - 1) {
760 focusIndex++;
761 redraw ();
762 }
763 } else {
764 if (focusIndex > 0) {
765 focusIndex--;
766 redraw ();
767 }
768 }
769 break;
770 default:
771 }
772 } else {
773 switch (wParam) {
774 case ' ':
775 case SWT.CR:
776 case SWT.TAB:
777 /*
778 * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR
779 * so that the key that was ignored during WM_KEYDOWN is processed.
780 * This allows the application to cancel an operation that is normally
781 * performed in WM_KEYDOWN from WM_CHAR.
782 */
783 int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
784 return new LRESULT (code);
785 default:
786 }
787
788 }
789 return result;
790 }
791
792 override LRESULT WM_GETDLGCODE (int wParam, int lParam) {
793 LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
794 if (result !is null) return result;
795 int index, count;
796 int /*long*/ code = 0;
797 if (OS.COMCTL32_MAJOR >= 6) {
798 LITEM item;
799 item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE;
800 item.stateMask = OS.LIS_FOCUSED;
801 index = 0;
802 while (OS.SendMessage (handle, OS.LM_GETITEM, 0, &item) !is 0) {
803 if ((item.state & OS.LIS_FOCUSED) !is 0) {
804 index = item.iLink;
805 }
806 item.iLink++;
807 }
808 count = item.iLink;
809 code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam);
810 } else {
811 index = focusIndex;
812 count = offsets.length;
813 }
814 if (count is 0) {
815 return new LRESULT (code | OS.DLGC_STATIC);
816 }
817 bool next = OS.GetKeyState (OS.VK_SHIFT) >= 0;
818 if (next && index < count - 1) {
819 return new LRESULT (code | OS.DLGC_WANTTAB);
820 }
821 if (!next && index > 0) {
822 return new LRESULT (code | OS.DLGC_WANTTAB);
823 }
824 return result;
825 }
826
827 override LRESULT WM_GETFONT (int wParam, int lParam) {
828 LRESULT result = super.WM_GETFONT (wParam, lParam);
829 if (result !is null) return result;
830 int /*long*/ code = callWindowProc (handle, OS.WM_GETFONT, wParam, lParam);
831 if (code !is 0) return new LRESULT (code);
832 if (font is null) font = defaultFont ();
833 return new LRESULT ( cast(int) font);
834 }
835
836 override LRESULT WM_KEYDOWN (int wParam, int lParam) {
837 LRESULT result = super.WM_KEYDOWN (wParam, lParam);
838 if (result !is null) return result;
839 if (OS.COMCTL32_MAJOR >= 6) {
840 switch (wParam) {
841 case OS.VK_SPACE:
842 case OS.VK_RETURN:
843 case OS.VK_TAB:
844 /*
845 * Ensure that the window proc does not process VK_SPACE,
846 * VK_RETURN or VK_TAB so that it can be handled in WM_CHAR.
847 * This allows the application to cancel an operation that
848 * is normally performed in WM_KEYDOWN from WM_CHAR.
849 */
850 return LRESULT.ZERO;
851 default:
852 }
853 }
854 return result;
855 }
856
857 override LRESULT WM_KILLFOCUS (int wParam, int lParam) {
858 LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
859 if (OS.COMCTL32_MAJOR < 6) redraw ();
860 return result;
861 }
862
863 override LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
864 LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
865 if (result is LRESULT.ZERO) return result;
866 if (OS.COMCTL32_MAJOR < 6) {
867 if (focusIndex !is -1) setFocus ();
868 int x = OS.GET_X_LPARAM (lParam);
869 int y = OS.GET_Y_LPARAM (lParam);
870 int offset = layout.getOffset (x, y, null);
871 int oldSelectionX = selection.x;
872 int oldSelectionY = selection.y;
873 selection.x = offset;
874 selection.y = -1;
875 if (oldSelectionX !is -1 && oldSelectionY !is -1) {
876 if (oldSelectionX > oldSelectionY) {
877 int temp = oldSelectionX;
878 oldSelectionX = oldSelectionY;
879 oldSelectionY = temp;
880 }
881 Rectangle rect = layout.getBounds (oldSelectionX, oldSelectionY);
882 redraw (rect.x, rect.y, rect.width, rect.height, false);
883 }
884 for (int j = 0; j < offsets.length; j++) {
885 Rectangle [] rects = getRectangles (j);
886 for (int i = 0; i < rects.length; i++) {
887 Rectangle rect = rects [i];
888 if (rect.contains (x, y)) {
889 if (j !is focusIndex) {
890 redraw ();
891 }
892 focusIndex = mouseDownIndex = j;
893 return result;
894 }
895 }
896 }
897 }
898 return result;
899 }
900
901 override LRESULT WM_LBUTTONUP (int wParam, int lParam) {
902 LRESULT result = super.WM_LBUTTONUP (wParam, lParam);
903 if (result is LRESULT.ZERO) return result;
904 if (OS.COMCTL32_MAJOR < 6) {
905 if (mouseDownIndex is -1) return result;
906 int x = OS.GET_X_LPARAM (lParam);
907 int y = OS.GET_Y_LPARAM (lParam);
908 Rectangle [] rects = getRectangles (mouseDownIndex);
909 for (int i = 0; i < rects.length; i++) {
910 Rectangle rect = rects [i];
911 if (rect.contains (x, y)) {
912 Event event = new Event ();
913 event.text = ids [mouseDownIndex];
914 sendEvent (SWT.Selection, event);
915 break;
916 }
917 }
918 }
919 mouseDownIndex = -1;
920 return result;
921 }
922
923 override LRESULT WM_NCHITTEST (int wParam, int lParam) {
924 LRESULT result = super.WM_NCHITTEST (wParam, lParam);
925 if (result !is null) return result;
926
927 /*
928 * Feature in Windows. For WM_NCHITTEST, the Syslink window proc
929 * returns HTTRANSPARENT when mouse is over plain text. The fix is
930 * to always return HTCLIENT.
931 */
932 if (OS.COMCTL32_MAJOR >= 6) return new LRESULT (OS.HTCLIENT);
933
934 return result;
935 }
936
937 override LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
938 LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
939 if (OS.COMCTL32_MAJOR < 6) {
940 int x = OS.GET_X_LPARAM (lParam);
941 int y = OS.GET_Y_LPARAM (lParam);
942 if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
943 int oldSelection = selection.y;
944 selection.y = layout.getOffset (x, y, null);
945 if (selection.y !is oldSelection) {
946 int newSelection = selection.y;
947 if (oldSelection > newSelection) {
948 int temp = oldSelection;
949 oldSelection = newSelection;
950 newSelection = temp;
951 }
952 Rectangle rect = layout.getBounds (oldSelection, newSelection);
953 redraw (rect.x, rect.y, rect.width, rect.height, false);
954 }
955 } else {
956 for (int j = 0; j < offsets.length; j++) {
957 Rectangle [] rects = getRectangles (j);
958 for (int i = 0; i < rects.length; i++) {
959 Rectangle rect = rects [i];
960 if (rect.contains (x, y)) {
961 setCursor (display.getSystemCursor (SWT.CURSOR_HAND));
962 return result;
963 }
964 }
965 }
966 setCursor (null);
967 }
968 }
969 return result;
970 }
971
972 override LRESULT WM_PAINT (int wParam, int lParam) {
973 if (OS.COMCTL32_MAJOR >= 6) {
974 return super.WM_PAINT (wParam, lParam);
975 }
976 PAINTSTRUCT ps;
977 GCData data = new GCData ();
978 data.ps = &ps;
979 data.hwnd = handle;
980 GC gc = new_GC (data);
981 if (gc !is null) {
982 int width = ps.rcPaint.right - ps.rcPaint.left;
983 int height = ps.rcPaint.bottom - ps.rcPaint.top;
984 if (width !is 0 && height !is 0) {
985 RECT rect;
986 OS.SetRect (&rect, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
987 drawWidget (gc, &rect);
988 }
989 gc.dispose ();
990 }
991 return LRESULT.ZERO;
992 }
993
994 override LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
995 LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
996 if (OS.COMCTL32_MAJOR < 6) {
997 RECT rect;
998 OS.GetClientRect (handle, &rect);
999 GCData data = new GCData ();
1000 data.device = display;
1001 data.foreground = getForegroundPixel ();
1002 GC gc = GC.win32_new (cast(HANDLE)wParam, data);
1003 drawWidget (gc, &rect);
1004 gc.dispose ();
1005 }
1006 return result;
1007 }
1008
1009 override LRESULT WM_SETFOCUS (int wParam, int lParam) {
1010 LRESULT result = super.WM_SETFOCUS (wParam, lParam);
1011 if (OS.COMCTL32_MAJOR < 6) redraw ();
1012 return result;
1013 }
1014
1015 override LRESULT WM_SETFONT (int wParam, int lParam) {
1016 if (OS.COMCTL32_MAJOR < 6) {
1017 layout.setFont (Font.win32_new (display, cast(HANDLE)wParam));
1018 }
1019 if (lParam !is 0) OS.InvalidateRect (handle, null, true);
1020 font = cast(HFONT) wParam;
1021 return super.WM_SETFONT (wParam, lParam);
1022 }
1023
1024 override LRESULT WM_SIZE (int wParam, int lParam) {
1025 LRESULT result = super.WM_SIZE (wParam, lParam);
1026 if (OS.COMCTL32_MAJOR < 6) {
1027 RECT rect;
1028 OS.GetClientRect (handle, &rect);
1029 layout.setWidth (rect.right > 0 ? rect.right : -1);
1030 redraw ();
1031 }
1032 return result;
1033 }
1034
1035 override LRESULT wmColorChild (int wParam, int lParam) {
1036 LRESULT result = super.wmColorChild (wParam, lParam);
1037 /*
1038 * Feature in Windows. When a SysLink is disabled, it does
1039 * not gray out the non-link portion of the text. The fix
1040 * is to set the text color to the system gray color.
1041 */
1042 if (OS.COMCTL32_MAJOR >= 6) {
1043 if (!OS.IsWindowEnabled (handle)) {
1044 OS.SetTextColor (cast(HANDLE)wParam, OS.GetSysColor (OS.COLOR_GRAYTEXT));
1045 if (result is null) {
1046 int backPixel = getBackgroundPixel ();
1047 OS.SetBkColor (cast(HANDLE)wParam, backPixel);
1048 auto hBrush = findBrush (backPixel, OS.BS_SOLID);
1049 return new LRESULT ( cast(int) hBrush);
1050 }
1051 }
1052 }
1053 return result;
1054 }
1055
1056 override LRESULT wmNotifyChild (NMHDR* hdr, int wParam, int lParam) {
1057 if (OS.COMCTL32_MAJOR >= 6) {
1058 switch (hdr.code) {
1059 case OS.NM_RETURN:
1060 case OS.NM_CLICK:
1061 NMLINK* item = cast(NMLINK*)lParam;
1062 //OS.MoveMemory (item, lParam, NMLINK.sizeof);
1063 Event event = new Event ();
1064 event.text = ids [item.item.iLink];
1065 sendEvent (SWT.Selection, event);
1066 break;
1067 default:
1068 }
1069 }
1070 return super.wmNotifyChild (hdr, wParam, lParam);
1071 }
1072 }
1073