Mercurial > projects > dwt2
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, <A> and </A>. 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 |