Mercurial > projects > dwt-addons
annotate dwtx/ui/forms/widgets/FormText.d @ 104:04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
These new wrappers now use the tango.util.containers instead of the tango.util.collections.
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Thu, 07 Aug 2008 15:01:33 +0200 |
parents | 56fea7e5f0f9 |
children | c3583c6ec027 |
rev | line source |
---|---|
75 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2007 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 dwtx.ui.forms.widgets.FormText; | |
14 | |
15 import dwtx.ui.forms.widgets.ILayoutExtension; | |
16 import dwtx.ui.forms.widgets.Form; | |
17 | |
18 import dwt.DWT; | |
19 import dwt.DWTException; | |
20 import dwt.accessibility.ACC; | |
21 import dwt.accessibility.Accessible; | |
22 import dwt.accessibility.AccessibleAdapter; | |
23 import dwt.accessibility.AccessibleControlAdapter; | |
24 import dwt.accessibility.AccessibleControlEvent; | |
25 import dwt.accessibility.AccessibleEvent; | |
26 import dwt.custom.ScrolledComposite; | |
27 import dwt.dnd.Clipboard; | |
28 import dwt.dnd.TextTransfer; | |
29 import dwt.dnd.Transfer; | |
30 import dwt.events.DisposeEvent; | |
31 import dwt.events.DisposeListener; | |
32 import dwt.events.FocusEvent; | |
33 import dwt.events.FocusListener; | |
34 import dwt.events.MenuEvent; | |
35 import dwt.events.MenuListener; | |
36 import dwt.events.MouseEvent; | |
37 import dwt.events.MouseListener; | |
38 import dwt.events.MouseMoveListener; | |
39 import dwt.events.MouseTrackListener; | |
40 import dwt.events.PaintEvent; | |
41 import dwt.events.PaintListener; | |
42 import dwt.events.SelectionAdapter; | |
43 import dwt.events.SelectionEvent; | |
44 import dwt.events.SelectionListener; | |
45 import dwt.graphics.Color; | |
46 import dwt.graphics.Font; | |
47 import dwt.graphics.FontMetrics; | |
48 import dwt.graphics.GC; | |
49 import dwt.graphics.Image; | |
50 import dwt.graphics.Point; | |
51 import dwt.graphics.Rectangle; | |
52 import dwt.widgets.Canvas; | |
53 import dwt.widgets.Composite; | |
54 import dwt.widgets.Control; | |
55 import dwt.widgets.Event; | |
56 import dwt.widgets.Layout; | |
57 import dwt.widgets.Listener; | |
58 import dwt.widgets.Menu; | |
59 import dwt.widgets.MenuItem; | |
60 import dwt.widgets.TypedListener; | |
61 import dwtx.core.runtime.ListenerList; | |
62 import dwtx.ui.forms.HyperlinkSettings; | |
63 import dwtx.ui.forms.events.HyperlinkEvent; | |
64 import dwtx.ui.forms.events.IHyperlinkListener; | |
65 import dwtx.ui.internal.forms.Messages; | |
66 import dwtx.ui.internal.forms.widgets.ControlSegment; | |
67 import dwtx.ui.internal.forms.widgets.FormFonts; | |
68 import dwtx.ui.internal.forms.widgets.FormTextModel; | |
69 import dwtx.ui.internal.forms.widgets.FormUtil; | |
70 import dwtx.ui.internal.forms.widgets.IFocusSelectable; | |
71 import dwtx.ui.internal.forms.widgets.IHyperlinkSegment; | |
72 import dwtx.ui.internal.forms.widgets.ImageSegment; | |
73 import dwtx.ui.internal.forms.widgets.Locator; | |
74 import dwtx.ui.internal.forms.widgets.Paragraph; | |
75 import dwtx.ui.internal.forms.widgets.ParagraphSegment; | |
76 import dwtx.ui.internal.forms.widgets.SelectionData; | |
77 import dwtx.ui.internal.forms.widgets.TextSegment; | |
78 | |
79 import dwt.dwthelper.utils; | |
80 import dwt.dwthelper.InputStream; | |
81 import tango.io.Stdout; | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
82 import dwtx.dwtxhelper.Collection; |
75 | 83 |
84 /** | |
85 * This class is a read-only text control that is capable of rendering wrapped | |
86 * text. Text can be rendered as-is or by parsing the formatting XML tags. | |
87 * Independently, words that start with http:// can be converted into hyperlinks | |
88 * on the fly. | |
89 * <p> | |
90 * When configured to use formatting XML, the control requires the root element | |
91 * <code>form</code> to be used. The following tags can be children of the | |
92 * <code>form</code> element: | |
93 * </p> | |
94 * <ul> | |
95 * <li><b>p </b>- for defining paragraphs. The following attributes are | |
96 * allowed: | |
97 * <ul> | |
98 * <li><b>vspace </b>- if set to 'false', no vertical space will be added | |
99 * (default is 'true')</li> | |
100 * </ul> | |
101 * </li> | |
102 * <li><b>li </b>- for defining list items. The following attributes are | |
103 * allowed: | |
104 * <ul> | |
105 * <li><b>vspace </b>- the same as with the <b>p </b> tag</li> | |
106 * <li><b>style </b>- could be 'bullet' (default), 'text' and 'image'</li> | |
107 * <li><b>value </b>- not used for 'bullet'. For text, it is the value of the | |
108 * text that is rendered as a bullet. For image, it is the href of the image to | |
109 * be rendered as a bullet.</li> | |
110 * <li><b>indent </b>- the number of pixels to indent the text in the list item | |
111 * </li> | |
112 * <li><b>bindent </b>- the number of pixels to indent the bullet itself</li> | |
113 * </ul> | |
114 * </li> | |
115 * </ul> | |
116 * <p> | |
117 * Text in paragraphs and list items will be wrapped according to the width of | |
118 * the control. The following tags can appear as children of either <b>p </b> or | |
119 * <b>li </b> elements: | |
120 * <ul> | |
121 * <li><b>img </b>- to render an image. Element accepts attribute 'href' that | |
122 * is a key to the <code>Image</code> set using 'setImage' method. Vertical | |
123 * position of image relative to surrounding text is optionally controlled by | |
124 * the attribute <b>align</b> that can have values <b>top</b>, <b>middle</b> | |
125 * and <b>bottom</b></li> | |
126 * <li><b>a </b>- to render a hyperlink. Element accepts attribute 'href' that | |
127 * will be provided to the hyperlink listeners via HyperlinkEvent object. The | |
128 * element also accepts 'nowrap' attribute (default is false). When set to | |
129 * 'true', the hyperlink will not be wrapped. Hyperlinks automatically created | |
130 * when 'http://' is encountered in text are not wrapped.</li> | |
131 * <li><b>b </b>- the enclosed text will use bold font.</li> | |
132 * <li><b>br </b>- forced line break (no attributes).</li> | |
133 * <li><b>span </b>- the enclosed text will have the color and font specified | |
134 * in the element attributes. Color is provided using 'color' attribute and is a | |
135 * key to the Color object set by 'setColor' method. Font is provided using | |
136 * 'font' attribute and is a key to the Font object set by 'setFont' method. As with | |
137 * hyperlinks, it is possible to block wrapping by setting 'nowrap' to true | |
138 * (false by default). | |
139 * </li> | |
140 * <li><b>control (new in 3.1)</b> - to place a control that is a child of the | |
141 * text control. Element accepts attribute 'href' that is a key to the Control | |
142 * object set using 'setControl' method. Optionally, attribute 'fill' can be set | |
143 * to <code>true</code> to make the control fill the entire width of the text. | |
144 * Form text is not responsible for creating or disposing controls, it only | |
145 * places them relative to the surrounding text. Similar to <b>img</b>, | |
146 * vertical position of the control can be set using the <b>align</b> | |
147 * attribute. In addition, <b>width</b> and <b>height</b> attributes can | |
148 * be used to force the dimensions of the control. If not used, | |
149 * the preferred control size will be used. | |
150 * </ul> | |
151 * <p> | |
152 * None of the elements can nest. For example, you cannot have <b>b </b> inside | |
153 * a <b>span </b>. This was done to keep everything simple and transparent. | |
154 * Since 3.1, an exception to this rule has been added to support nesting images | |
155 * and text inside the hyperlink tag (<b>a</b>). Image enclosed in the | |
156 * hyperlink tag acts as a hyperlink, can be clicked on and can accept and | |
157 * render selection focus. When both text and image is enclosed, selection and | |
158 * rendering will affect both as a single hyperlink. | |
159 * </p> | |
160 * <p> | |
161 * Since 3.1, it is possible to select text. Text selection can be | |
162 * programmatically accessed and also copied to clipboard. Non-textual objects | |
163 * (images, controls etc.) in the selection range are ignored. | |
164 * <p> | |
165 * Care should be taken when using this control. Form text is not an HTML | |
166 * browser and should not be treated as such. If you need complex formatting | |
167 * capabilities, use Browser widget. If you need editing capabilities and | |
168 * font/color styles of text segments is all you need, use StyleText widget. | |
169 * Finally, if all you need is to wrap text, use DWT Label widget and create it | |
170 * with DWT.WRAP style. | |
171 * | |
172 * @see FormToolkit | |
173 * @see TableWrapLayout | |
174 * @since 3.0 | |
175 */ | |
176 public class FormText : Canvas { | |
177 /** | |
178 * The object ID to be used when registering action to handle URL hyperlinks | |
179 * (those that should result in opening the web browser). Value is | |
180 * "urlHandler". | |
181 */ | |
182 public static const String URL_HANDLER_ID = "urlHandler"; //$NON-NLS-1$ | |
183 | |
184 /** | |
185 * Value of the horizontal margin (default is 0). | |
186 */ | |
187 public int marginWidth = 0; | |
188 | |
189 /** | |
190 * Value of tue vertical margin (default is 1). | |
191 */ | |
192 public int marginHeight = 1; | |
193 | |
194 // private fields | |
195 private static const bool DEBUG_TEXT = false;//"true".equalsIgnoreCase(Platform.getDebugOption(FormUtil.DEBUG_TEXT)); | |
196 private static const bool DEBUG_TEXTSIZE = false;//"true".equalsIgnoreCase(Platform.getDebugOption(FormUtil.DEBUG_TEXTSIZE)); | |
197 | |
198 private static const bool DEBUG_FOCUS = false;//"true".equalsIgnoreCase(Platform.getDebugOption(FormUtil.DEBUG_FOCUS)); | |
199 | |
200 private bool hasFocus; | |
201 | |
202 private bool paragraphsSeparated = true; | |
203 | |
204 private FormTextModel model; | |
205 | |
206 private ListenerList listeners; | |
207 | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
208 private Hashtable resourceTable; |
75 | 209 |
210 private IHyperlinkSegment entered; | |
211 | |
212 private IHyperlinkSegment armed; | |
213 | |
214 private bool mouseFocus = false; | |
215 | |
216 private bool controlFocusTransfer = false; | |
217 | |
218 private bool inSelection = false; | |
219 | |
220 private SelectionData selData; | |
221 | |
222 private static const String INTERNAL_MENU = "__internal_menu__"; //$NON-NLS-1$ | |
223 | |
224 private static const String CONTROL_KEY = "__segment__"; //$NON-NLS-1$ | |
225 | |
226 private class FormTextLayout : Layout, ILayoutExtension { | |
227 public this() { | |
228 } | |
229 | |
230 public int computeMaximumWidth(Composite parent, bool changed) { | |
231 return computeSize(parent, DWT.DEFAULT, DWT.DEFAULT, changed).x; | |
232 } | |
233 | |
234 public int computeMinimumWidth(Composite parent, bool changed) { | |
235 return computeSize(parent, 5, DWT.DEFAULT, true).x; | |
236 } | |
237 | |
238 /* | |
239 * @see Layout#computeSize(Composite, int, int, bool) | |
240 */ | |
241 public Point computeSize(Composite composite, int wHint, int hHint, | |
242 bool changed) { | |
243 long start = 0; | |
244 | |
245 if (DEBUG_TEXT) | |
246 start = System.currentTimeMillis(); | |
247 int innerWidth = wHint; | |
248 if (innerWidth !is DWT.DEFAULT) | |
249 innerWidth -= marginWidth * 2; | |
250 Point textSize = computeTextSize(innerWidth); | |
251 int textWidth = textSize.x + 2 * marginWidth; | |
252 int textHeight = textSize.y + 2 * marginHeight; | |
253 Point result = new Point(textWidth, textHeight); | |
254 if (DEBUG_TEXT) { | |
255 long stop = System.currentTimeMillis(); | |
256 Stdout.formatln("FormText computeSize: {}ms", (stop - start)); //$NON-NLS-1$ | |
257 } | |
258 if (DEBUG_TEXTSIZE) { | |
259 Stdout.formatln("FormText ({}), computeSize: wHint={}, result={}", model.getAccessibleText(), wHint, result); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
260 } | |
261 return result; | |
262 } | |
263 | |
264 private Point computeTextSize(int wHint) { | |
265 Paragraph[] paragraphs = model.getParagraphs(); | |
266 GC gc = new GC(this.outer); | |
267 gc.setFont(getFont()); | |
268 Locator loc = new Locator(); | |
269 int width = wHint !is DWT.DEFAULT ? wHint : 0; | |
270 FontMetrics fm = gc.getFontMetrics(); | |
271 int lineHeight = fm.getHeight(); | |
272 bool selectableInTheLastRow = false; | |
273 for (int i = 0; i < paragraphs.length; i++) { | |
274 Paragraph p = paragraphs[i]; | |
275 if (i > 0 && getParagraphsSeparated() | |
276 && p.getAddVerticalSpace()) | |
277 loc.y += getParagraphSpacing(lineHeight); | |
278 loc.rowHeight = 0; | |
279 loc.indent = p.getIndent(); | |
280 loc.x = p.getIndent(); | |
281 ParagraphSegment[] segments = p.getSegments(); | |
282 if (segments.length > 0) { | |
283 selectableInTheLastRow = false; | |
284 int pwidth = 0; | |
285 for (int j = 0; j < segments.length; j++) { | |
286 ParagraphSegment segment = segments[j]; | |
287 segment.advanceLocator(gc, wHint, loc, resourceTable, | |
288 false); | |
289 if (wHint !is DWT.DEFAULT) { | |
290 width = Math.max(width, loc.width); | |
291 } else { | |
292 pwidth += loc.width; | |
293 } | |
294 if (null !is cast(IFocusSelectable)segment ) | |
295 selectableInTheLastRow = true; | |
296 } | |
297 if (wHint is DWT.DEFAULT) | |
298 width = Math.max(width, pwidth); | |
299 loc.y += loc.rowHeight; | |
300 } else { | |
301 // empty new line | |
302 loc.y += lineHeight; | |
303 } | |
304 } | |
305 gc.dispose(); | |
306 if (selectableInTheLastRow) | |
307 loc.y += 1; | |
308 return new Point(width, loc.y); | |
309 } | |
310 | |
311 protected void layout(Composite composite, bool flushCache) { | |
312 long start = 0; | |
313 | |
314 if (DEBUG_TEXT) { | |
315 start = System.currentTimeMillis(); | |
316 } | |
317 selData = null; | |
318 Rectangle carea = composite.getClientArea(); | |
319 if (DEBUG_TEXTSIZE) { | |
320 Stdout.formatln("FormText layout ({}), carea={}",model.getAccessibleText(),carea); //$NON-NLS-1$ //$NON-NLS-2$ | |
321 } | |
322 GC gc = new GC(composite); | |
323 gc.setFont(getFont()); | |
324 ensureBoldFontPresent(getFont()); | |
325 gc.setForeground(getForeground()); | |
326 gc.setBackground(getBackground()); | |
327 | |
328 Locator loc = new Locator(); | |
329 loc.marginWidth = marginWidth; | |
330 loc.marginHeight = marginHeight; | |
331 loc.x = marginWidth; | |
332 loc.y = marginHeight; | |
333 FontMetrics fm = gc.getFontMetrics(); | |
334 int lineHeight = fm.getHeight(); | |
335 | |
336 Paragraph[] paragraphs = model.getParagraphs(); | |
337 IHyperlinkSegment selectedLink = getSelectedLink(); | |
338 for (int i = 0; i < paragraphs.length; i++) { | |
339 Paragraph p = paragraphs[i]; | |
340 if (i > 0 && paragraphsSeparated && p.getAddVerticalSpace()) | |
341 loc.y += getParagraphSpacing(lineHeight); | |
342 loc.indent = p.getIndent(); | |
343 loc.resetCaret(); | |
344 loc.rowHeight = 0; | |
345 p.layout(gc, carea.width, loc, lineHeight, resourceTable, | |
346 selectedLink); | |
347 } | |
348 gc.dispose(); | |
349 if (DEBUG_TEXT) { | |
350 long stop = System.currentTimeMillis(); | |
351 Stdout.formatln("FormText.layout: {}ms", (stop - start)); //$NON-NLS-1$ //$NON-NLS-2$ | |
352 } | |
353 } | |
354 } | |
355 | |
356 /** | |
357 * Contructs a new form text widget in the provided parent and using the | |
358 * styles. | |
359 * | |
360 * @param parent | |
361 * form text parent control | |
362 * @param style | |
363 * the widget style | |
364 */ | |
365 public this(Composite parent, int style) { | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
366 resourceTable = new Hashtable(); |
75 | 367 super(parent, DWT.NO_BACKGROUND | DWT.WRAP | style); |
368 setLayout(new FormTextLayout()); | |
369 model = new FormTextModel(); | |
370 addDisposeListener(new class DisposeListener { | |
371 public void widgetDisposed(DisposeEvent e) { | |
372 model.dispose(); | |
373 disposeResourceTable(true); | |
374 } | |
375 }); | |
376 addPaintListener(new class PaintListener { | |
377 public void paintControl(PaintEvent e) { | |
378 paint(e); | |
379 } | |
380 }); | |
381 addListener(DWT.KeyDown, new class Listener { | |
382 public void handleEvent(Event e) { | |
383 if (e.character is '\r') { | |
384 activateSelectedLink(); | |
385 return; | |
386 } | |
387 } | |
388 }); | |
389 addListener(DWT.Traverse, new class Listener { | |
390 public void handleEvent(Event e) { | |
391 if (DEBUG_FOCUS) | |
392 Stdout.formatln("Traversal: {}", e); //$NON-NLS-1$ | |
393 switch (e.detail) { | |
394 case DWT.TRAVERSE_PAGE_NEXT: | |
395 case DWT.TRAVERSE_PAGE_PREVIOUS: | |
396 case DWT.TRAVERSE_ARROW_NEXT: | |
397 case DWT.TRAVERSE_ARROW_PREVIOUS: | |
398 e.doit = false; | |
399 return; | |
400 } | |
401 if (!model.hasFocusSegments()) { | |
402 e.doit = true; | |
403 return; | |
404 } | |
405 if (e.detail is DWT.TRAVERSE_TAB_NEXT) | |
406 e.doit = advance(true); | |
407 else if (e.detail is DWT.TRAVERSE_TAB_PREVIOUS) | |
408 e.doit = advance(false); | |
409 else if (e.detail !is DWT.TRAVERSE_RETURN) | |
410 e.doit = true; | |
411 } | |
412 }); | |
413 addFocusListener(new class FocusListener { | |
414 public void focusGained(FocusEvent e) { | |
415 if (!hasFocus) { | |
416 hasFocus = true; | |
417 if (DEBUG_FOCUS) { | |
418 Stdout.formatln("FormText: focus gained"); //$NON-NLS-1$ | |
419 } | |
420 if (!mouseFocus && !controlFocusTransfer) { | |
421 handleFocusChange(); | |
422 } | |
423 } | |
424 } | |
425 | |
426 public void focusLost(FocusEvent e) { | |
427 if (DEBUG_FOCUS) { | |
428 Stdout.formatln("FormText: focus lost"); //$NON-NLS-1$ | |
429 } | |
430 if (hasFocus) { | |
431 hasFocus = false; | |
432 if (!controlFocusTransfer) | |
433 handleFocusChange(); | |
434 } | |
435 } | |
436 }); | |
437 addMouseListener(new class MouseListener { | |
438 public void mouseDoubleClick(MouseEvent e) { | |
439 } | |
440 | |
441 public void mouseDown(MouseEvent e) { | |
442 // select a link | |
443 handleMouseClick(e, true); | |
444 } | |
445 | |
446 public void mouseUp(MouseEvent e) { | |
447 // activate a link | |
448 handleMouseClick(e, false); | |
449 } | |
450 }); | |
451 addMouseTrackListener(new class MouseTrackListener { | |
452 public void mouseEnter(MouseEvent e) { | |
453 handleMouseMove(e); | |
454 } | |
455 | |
456 public void mouseExit(MouseEvent e) { | |
457 if (entered !is null) { | |
458 exitLink(entered, e.stateMask); | |
459 paintLinkHover(entered, false); | |
460 entered = null; | |
461 setCursor(null); | |
462 } | |
463 } | |
464 | |
465 public void mouseHover(MouseEvent e) { | |
466 handleMouseHover(e); | |
467 } | |
468 }); | |
469 addMouseMoveListener(new class MouseMoveListener { | |
470 public void mouseMove(MouseEvent e) { | |
471 handleMouseMove(e); | |
472 } | |
473 }); | |
474 initAccessible(); | |
475 ensureBoldFontPresent(getFont()); | |
476 createMenu(); | |
477 // we will handle traversal of controls, if any | |
478 setTabList(cast(Control[])null); | |
479 } | |
480 | |
481 /** | |
482 * Test for focus. | |
483 * | |
484 * @return <samp>true </samp> if the widget has focus. | |
485 */ | |
486 public bool getFocus() { | |
487 return hasFocus; | |
488 } | |
489 | |
490 /** | |
491 * Test if the widget is currently processing the text it is about to | |
492 * render. | |
493 * | |
494 * @return <samp>true </samp> if the widget is still loading the text, | |
495 * <samp>false </samp> otherwise. | |
496 * @deprecated not used any more - returns <code>false</code> | |
497 */ | |
498 public bool isLoading() { | |
499 return false; | |
500 } | |
501 | |
502 /** | |
503 * Returns the text that will be shown in the control while the real content | |
504 * is loading. | |
505 * | |
506 * @return loading text message | |
507 * @deprecated loading text is not used since 3.1 | |
508 */ | |
509 public String getLoadingText() { | |
510 return null; | |
511 } | |
512 | |
513 /** | |
514 * Sets the text that will be shown in the control while the real content is | |
515 * loading. This is significant when content to render is loaded from the | |
516 * input stream that was created from a remote URL, and the time to load the | |
517 * entire content is nontrivial. | |
518 * | |
519 * @param loadingText | |
520 * loading text message | |
521 * @deprecated use setText(loadingText, false, false); | |
522 */ | |
523 public void setLoadingText(String loadingText) { | |
524 setText(loadingText, false, false); | |
525 } | |
526 | |
527 /** | |
528 * If paragraphs are separated, spacing will be added between them. | |
529 * Otherwise, new paragraphs will simply start on a new line with no | |
530 * spacing. | |
531 * | |
532 * @param value | |
533 * <samp>true </samp> if paragraphs are separated, </samp> false | |
534 * </samp> otherwise. | |
535 */ | |
536 public void setParagraphsSeparated(bool value) { | |
537 paragraphsSeparated = value; | |
538 } | |
539 | |
540 /** | |
541 * Tests if there is some inter-paragraph spacing. | |
542 * | |
543 * @return <samp>true </samp> if paragraphs are separated, <samp>false | |
544 * </samp> otherwise. | |
545 */ | |
546 public bool getParagraphsSeparated() { | |
547 return paragraphsSeparated; | |
548 } | |
549 | |
550 /** | |
551 * Registers the image referenced by the provided key. | |
552 * <p> | |
553 * For <samp>img </samp> tags, an object of a type <samp>Image </samp> must | |
554 * be registered using the key equivalent to the value of the <samp>href | |
555 * </samp> attribute used in the tag. | |
556 * | |
557 * @param key | |
558 * unique key that matches the value of the <samp>href </samp> | |
559 * attribute. | |
560 * @param image | |
561 * an object of a type <samp>Image </samp>. | |
562 */ | |
563 public void setImage(String key, Image image) { | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
564 resourceTable.put("i." ~ key, image); //$NON-NLS-1$ |
75 | 565 } |
566 | |
567 /** | |
568 * Registers the color referenced by the provided key. | |
569 * <p> | |
570 * For <samp>span </samp> tags, an object of a type <samp>Color </samp> must | |
571 * be registered using the key equivalent to the value of the <samp>color | |
572 * </samp> attribute. | |
573 * | |
574 * @param key | |
575 * unique key that matches the value of the <samp>color </samp> | |
576 * attribute. | |
577 * @param color | |
578 * an object of the type <samp>Color </samp> or <samp>null</samp> | |
579 * if the key needs to be cleared. | |
580 */ | |
581 public void setColor(String key, Color color) { | |
582 String fullKey = "c." ~ key; //$NON-NLS-1$ | |
583 if (color is null) | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
584 resourceTable.remove(fullKey); |
75 | 585 else |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
586 resourceTable.put(fullKey, color); |
75 | 587 } |
588 | |
589 /** | |
590 * Registers the font referenced by the provided key. | |
591 * <p> | |
592 * For <samp>span </samp> tags, an object of a type <samp>Font </samp> must | |
593 * be registered using the key equivalent to the value of the <samp>font | |
594 * </samp> attribute. | |
595 * | |
596 * @param key | |
597 * unique key that matches the value of the <samp>font </samp> | |
598 * attribute. | |
599 * @param font | |
600 * an object of the type <samp>Font </samp> or <samp>null</samp> | |
601 * if the key needs to be cleared. | |
602 */ | |
603 public void setFont(String key, Font font) { | |
604 String fullKey = "f." ~ key; //$NON-NLS-1$ | |
605 if (font is null) | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
606 resourceTable.remove(fullKey); |
75 | 607 else |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
608 resourceTable.put(fullKey, font); |
75 | 609 model.clearCache(fullKey); |
610 } | |
611 | |
612 /** | |
613 * Registers the control referenced by the provided key. | |
614 * <p> | |
615 * For <samp>control</samp> tags, an object of a type <samp>Control</samp> | |
616 * must be registered using the key equivalent to the value of the | |
617 * <samp>control</samp> attribute. | |
618 * | |
619 * @param key | |
620 * unique key that matches the value of the <samp>control</samp> | |
621 * attribute. | |
622 * @param control | |
623 * an object of the type <samp>Control</samp> or <samp>null</samp> | |
624 * if the existing control at the specified key needs to be | |
625 * removed. | |
626 * @since 3.1 | |
627 */ | |
628 public void setControl(String key, Control control) { | |
629 String fullKey = "o." ~ key; //$NON-NLS-1$ | |
630 if (control is null) | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
631 resourceTable.remove(fullKey); |
75 | 632 else |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
633 resourceTable.put(fullKey, control); |
75 | 634 } |
635 | |
636 /** | |
637 * Sets the font to use to render the default text (text that does not have | |
638 * special font property assigned). Bold font will be constructed from this | |
639 * font. | |
640 * | |
641 * @param font | |
642 * the default font to use | |
643 */ | |
644 public void setFont(Font font) { | |
645 super.setFont(font); | |
646 model.clearCache(null); | |
647 Font boldFont = cast(Font) resourceTable.get(FormTextModel.BOLD_FONT_ID); | |
648 if (boldFont !is null) { | |
649 FormFonts.getInstance().markFinished(boldFont); | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
650 resourceTable.remove(FormTextModel.BOLD_FONT_ID); |
75 | 651 } |
652 ensureBoldFontPresent(getFont()); | |
653 } | |
654 | |
655 /** | |
656 * Sets the provided text. Text can be rendered as-is, or by parsing the | |
657 * formatting tags. Optionally, sections of text starting with http:// will | |
658 * be converted to hyperlinks. | |
659 * | |
660 * @param text | |
661 * the text to render | |
662 * @param parseTags | |
663 * if <samp>true </samp>, formatting tags will be parsed. | |
664 * Otherwise, text will be rendered as-is. | |
665 * @param expandURLs | |
666 * if <samp>true </samp>, URLs found in the untagged text will be | |
667 * converted into hyperlinks. | |
668 */ | |
669 public void setText(String text, bool parseTags, bool expandURLs) { | |
670 disposeResourceTable(false); | |
671 entered = null; | |
672 if (parseTags) | |
673 model.parseTaggedText(text, expandURLs); | |
674 else | |
675 model.parseRegularText(text, expandURLs); | |
676 hookControlSegmentFocus(); | |
677 layout(); | |
678 redraw(); | |
679 } | |
680 | |
681 /** | |
682 * Sets the contents of the stream. Optionally, URLs in untagged text can be | |
683 * converted into hyperlinks. The caller is responsible for closing the | |
684 * stream. | |
685 * | |
686 * @param is | |
687 * stream to render | |
688 * @param expandURLs | |
689 * if <samp>true </samp>, URLs found in untagged text will be | |
690 * converted into hyperlinks. | |
691 */ | |
692 public void setContents(InputStream is_, bool expandURLs) { | |
693 entered = null; | |
694 disposeResourceTable(false); | |
695 model.parseInputStream(is_, expandURLs); | |
696 hookControlSegmentFocus(); | |
697 layout(); | |
698 redraw(); | |
699 } | |
700 | |
701 private void hookControlSegmentFocus() { | |
702 Paragraph[] paragraphs = model.getParagraphs(); | |
703 if (paragraphs is null) | |
704 return; | |
705 Listener listener = new class Listener { | |
706 public void handleEvent(Event e) { | |
707 switch (e.type) { | |
708 case DWT.FocusIn: | |
709 if (!controlFocusTransfer) | |
710 syncControlSegmentFocus(cast(Control) e.widget); | |
711 break; | |
712 case DWT.Traverse: | |
713 if (DEBUG_FOCUS) | |
714 Stdout.formatln("Control traversal: {}", e); //$NON-NLS-1$ | |
715 switch (e.detail) { | |
716 case DWT.TRAVERSE_PAGE_NEXT: | |
717 case DWT.TRAVERSE_PAGE_PREVIOUS: | |
718 case DWT.TRAVERSE_ARROW_NEXT: | |
719 case DWT.TRAVERSE_ARROW_PREVIOUS: | |
720 e.doit = false; | |
721 return; | |
722 } | |
723 Control c = cast(Control) e.widget; | |
724 ControlSegment segment = cast(ControlSegment) c | |
725 .getData(CONTROL_KEY); | |
726 if (e.detail is DWT.TRAVERSE_TAB_NEXT) | |
727 e.doit = advanceControl(c, segment, true); | |
728 else if (e.detail is DWT.TRAVERSE_TAB_PREVIOUS) | |
729 e.doit = advanceControl(c, segment, false); | |
730 if (!e.doit) | |
731 e.detail = DWT.TRAVERSE_NONE; | |
732 break; | |
733 } | |
734 } | |
735 }; | |
736 for (int i = 0; i < paragraphs.length; i++) { | |
737 Paragraph p = paragraphs[i]; | |
738 ParagraphSegment[] segments = p.getSegments(); | |
739 for (int j = 0; j < segments.length; j++) { | |
740 if (auto cs = cast(ControlSegment)segments[j] ) { | |
741 Control c = cs.getControl(resourceTable); | |
742 if (c !is null) { | |
743 if (c.getData(CONTROL_KEY) is null) { | |
744 // first time - hook | |
745 c.setData(CONTROL_KEY, cs); | |
746 attachTraverseListener(c, listener); | |
747 } | |
748 } | |
749 } | |
750 } | |
751 } | |
752 } | |
753 | |
754 private void attachTraverseListener(Control c, Listener listener) { | |
755 if ( auto parent = cast(Composite) c ) { | |
756 Control[] children = parent.getChildren(); | |
757 for (int i = 0; i < children.length; i++) { | |
758 attachTraverseListener(children[i], listener); | |
759 } | |
760 if (auto canv = cast(Canvas)c ) { | |
761 // If Canvas, the control iteself can accept | |
762 // traverse events and should be monitored | |
763 c.addListener(DWT.Traverse, listener); | |
764 c.addListener(DWT.FocusIn, listener); | |
765 } | |
766 } else { | |
767 c.addListener(DWT.Traverse, listener); | |
768 c.addListener(DWT.FocusIn, listener); | |
769 } | |
770 } | |
771 | |
772 /** | |
773 * If we click on the control randomly, our internal book-keeping will be | |
774 * off. We need to update the model and mark the control segment and | |
775 * currently selected. Hyperlink that may have had focus must also be | |
776 * exited. | |
777 * | |
778 * @param control | |
779 * the control that got focus | |
780 */ | |
781 private void syncControlSegmentFocus(Control control) { | |
782 ControlSegment cs = null; | |
783 | |
784 while (control !is null) { | |
785 cs = cast(ControlSegment) control.getData(CONTROL_KEY); | |
786 if (cs !is null) | |
787 break; | |
788 control = control.getParent(); | |
789 } | |
790 if (cs is null) | |
791 return; | |
792 IFocusSelectable current = model.getSelectedSegment(); | |
793 // If the model and the control match, all is well | |
794 if (current is cs) | |
795 return; | |
796 IHyperlinkSegment oldLink = null; | |
797 if (current !is null && null !is cast(IHyperlinkSegment)current ) { | |
798 oldLink = cast(IHyperlinkSegment) current; | |
799 exitLink(oldLink, DWT.NULL); | |
800 } | |
801 if (DEBUG_FOCUS) | |
802 Stdout.formatln("Sync control: {}, oldLink={}", cs, oldLink); //$NON-NLS-1$ //$NON-NLS-2$ | |
803 model.select(cs); | |
804 if (oldLink !is null) | |
805 paintFocusTransfer(oldLink, null); | |
806 // getAccessible().setFocus(model.getSelectedSegmentIndex()); | |
807 } | |
808 | |
809 private bool advanceControl(Control c, ControlSegment segment, | |
810 bool next) { | |
811 Composite parent = c.getParent(); | |
812 if (parent is this) { | |
813 // segment-level control | |
814 IFocusSelectable nextSegment = model.getNextFocusSegment(next); | |
815 if (nextSegment !is null) { | |
816 controlFocusTransfer = true; | |
817 super.forceFocus(); | |
818 controlFocusTransfer = false; | |
819 model.select(segment); | |
820 return advance(next); | |
821 } | |
822 // nowhere to go | |
823 return setFocusToNextSibling(this, next); | |
824 } | |
825 if (setFocusToNextSibling(c, next)) | |
826 return true; | |
827 // still here - must go one level up | |
828 segment = cast(ControlSegment) parent.getData(CONTROL_KEY); | |
829 return advanceControl(parent, segment, next); | |
830 } | |
831 | |
832 private bool setFocusToNextSibling(Control c, bool next) { | |
833 Composite parent = c.getParent(); | |
834 Control[] children = parent.getTabList(); | |
835 for (int i = 0; i < children.length; i++) { | |
836 Control child = children[i]; | |
837 if (child is c) { | |
838 // here | |
839 if (next) { | |
840 for (int j = i + 1; j < children.length; j++) { | |
841 Control nc = children[j]; | |
842 if (nc.setFocus()) | |
843 return false; | |
844 } | |
845 } else { | |
846 for (int j = i - 1; j >= 0; j--) { | |
847 Control pc = children[j]; | |
848 if (pc.setFocus()) | |
849 return false; | |
850 } | |
851 } | |
852 } | |
853 } | |
854 return false; | |
855 } | |
856 | |
857 /** | |
858 * Controls whether whitespace inside paragraph and list items is | |
859 * normalized. Note that the new value will not affect the current text in | |
860 * the control, only subsequent calls to <code>setText</code> or | |
861 * <code>setContents</code>. | |
862 * <p> | |
863 * If normalized: | |
864 * <ul> | |
865 * <li>all white space characters will be condensed into at most one when | |
866 * between words.</li> | |
867 * <li>new line characters will be ignored and replaced with one white | |
868 * space character</li> | |
869 * <li>white space characters after the opening tags and before the closing | |
870 * tags will be trimmed</li> | |
871 * | |
872 * @param value | |
873 * <code>true</code> if whitespace is normalized, | |
874 * <code>false</code> otherwise. | |
875 */ | |
876 public void setWhitespaceNormalized(bool value) { | |
877 model.setWhitespaceNormalized(value); | |
878 } | |
879 | |
880 /** | |
881 * Tests whether whitespace inside paragraph and list item is normalized. | |
882 * | |
883 * @see #setWhitespaceNormalized(bool) | |
884 * @return <code>true</code> if whitespace is normalized, | |
885 * <code>false</code> otherwise. | |
886 */ | |
887 public bool isWhitespaceNormalized() { | |
888 return model.isWhitespaceNormalized(); | |
889 } | |
890 | |
891 /** | |
892 * Disposes the internal menu if created and sets the menu provided as a | |
893 * parameter. | |
894 * | |
895 * @param menu | |
896 * the menu to associate with this text control | |
897 */ | |
898 public void setMenu(Menu menu) { | |
899 Menu currentMenu = super.getMenu(); | |
900 if (currentMenu !is null && INTERNAL_MENU.equals(stringcast(currentMenu.getData()))) { | |
901 // internal menu set | |
902 if (menu !is null) { | |
903 currentMenu.dispose(); | |
904 super.setMenu(menu); | |
905 } | |
906 } else | |
907 super.setMenu(menu); | |
908 } | |
909 | |
910 private void createMenu() { | |
911 Menu menu = new Menu(this); | |
912 final MenuItem copyItem = new MenuItem(menu, DWT.PUSH); | |
913 copyItem.setText(Messages.FormText_copy); | |
914 | |
915 SelectionListener listener = new class SelectionAdapter { | |
916 public void widgetSelected(SelectionEvent e) { | |
917 if (e.widget is copyItem) { | |
918 copy(); | |
919 } | |
920 } | |
921 }; | |
922 copyItem.addSelectionListener(listener); | |
923 menu.addMenuListener(new class MenuListener { | |
924 public void menuShown(MenuEvent e) { | |
925 copyItem.setEnabled(canCopy()); | |
926 } | |
927 | |
928 public void menuHidden(MenuEvent e) { | |
929 } | |
930 }); | |
931 menu.setData(stringcast(INTERNAL_MENU)); | |
932 super.setMenu(menu); | |
933 } | |
934 | |
935 /** | |
936 * Returns the hyperlink settings that are in effect for this control. | |
937 * | |
938 * @return current hyperlinks settings | |
939 */ | |
940 public HyperlinkSettings getHyperlinkSettings() { | |
941 return model.getHyperlinkSettings(); | |
942 } | |
943 | |
944 /** | |
945 * Sets the hyperlink settings to be used for this control. Settings will | |
946 * affect things like hyperlink color, rendering style, cursor etc. | |
947 * | |
948 * @param settings | |
949 * hyperlink settings for this control | |
950 */ | |
951 public void setHyperlinkSettings(HyperlinkSettings settings) { | |
952 model.setHyperlinkSettings(settings); | |
953 } | |
954 | |
955 /** | |
956 * Adds a listener that will handle hyperlink events. | |
957 * | |
958 * @param listener | |
959 * the listener to add | |
960 */ | |
961 public void addHyperlinkListener(IHyperlinkListener listener) { | |
962 if (listeners is null) | |
963 listeners = new ListenerList(); | |
964 listeners.add(cast(Object)listener); | |
965 } | |
966 | |
967 /** | |
968 * Removes the hyperlink listener. | |
969 * | |
970 * @param listener | |
971 * the listener to remove | |
972 */ | |
973 public void removeHyperlinkListener(IHyperlinkListener listener) { | |
974 if (listeners is null) | |
975 return; | |
976 listeners.remove(cast(Object)listener); | |
977 } | |
978 | |
979 /** | |
980 * Adds a selection listener. A Selection event is sent by the widget when | |
981 * the selection has changed. | |
982 * <p> | |
983 * <code>widgetDefaultSelected</code> is not called for FormText. | |
984 * </p> | |
985 * | |
986 * @param listener | |
987 * the listener | |
988 * @exception DWTException | |
989 * <ul> | |
990 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been | |
991 * disposed</li> | |
992 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the | |
993 * thread that created the receiver</li> | |
994 * </ul> | |
995 * @exception IllegalArgumentException | |
996 * <ul> | |
997 * <li>ERROR_NULL_ARGUMENT when listener is null</li> | |
998 * </ul> | |
999 * @since 3.1 | |
1000 */ | |
1001 public void addSelectionListener(SelectionListener listener) { | |
1002 checkWidget(); | |
1003 if (listener is null) { | |
1004 DWT.error(DWT.ERROR_NULL_ARGUMENT); | |
1005 } | |
1006 TypedListener typedListener = new TypedListener(listener); | |
1007 addListener(DWT.Selection, typedListener); | |
1008 } | |
1009 | |
1010 /** | |
1011 * Removes the specified selection listener. | |
1012 * <p> | |
1013 * | |
1014 * @param listener | |
1015 * the listener | |
1016 * @exception DWTException | |
1017 * <ul> | |
1018 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been | |
1019 * disposed</li> | |
1020 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the | |
1021 * thread that created the receiver</li> | |
1022 * </ul> | |
1023 * @exception IllegalArgumentException | |
1024 * <ul> | |
1025 * <li>ERROR_NULL_ARGUMENT when listener is null</li> | |
1026 * </ul> | |
1027 * @since 3.1 | |
1028 */ | |
1029 public void removeSelectionListener(SelectionListener listener) { | |
1030 checkWidget(); | |
1031 if (listener is null) { | |
1032 DWT.error(DWT.ERROR_NULL_ARGUMENT); | |
1033 } | |
1034 removeListener(DWT.Selection, listener); | |
1035 } | |
1036 | |
1037 /** | |
1038 * Returns the selected text. | |
1039 * <p> | |
1040 * | |
1041 * @return selected text, or an empty String if there is no selection. | |
1042 * @exception DWTException | |
1043 * <ul> | |
1044 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been | |
1045 * disposed</li> | |
1046 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the | |
1047 * thread that created the receiver</li> | |
1048 * </ul> | |
1049 * @since 3.1 | |
1050 */ | |
1051 | |
1052 public String getSelectionText() { | |
1053 checkWidget(); | |
1054 if (selData !is null) | |
1055 return selData.getSelectionText(); | |
1056 return ""; //$NON-NLS-1$ | |
1057 } | |
1058 | |
1059 /** | |
1060 * Tests if the text is selected and can be copied into the clipboard. | |
1061 * | |
1062 * @return <code>true</code> if the selected text can be copied into the | |
1063 * clipboard, <code>false</code> otherwise. | |
1064 * @since 3.1 | |
1065 */ | |
1066 public bool canCopy() { | |
1067 return selData !is null && selData.canCopy(); | |
1068 } | |
1069 | |
1070 /** | |
1071 * Copies the selected text into the clipboard. Does nothing if no text is | |
1072 * selected or the text cannot be copied for any other reason. | |
1073 * | |
1074 * @since 3.1 | |
1075 */ | |
1076 | |
1077 public void copy() { | |
1078 if (!canCopy()) | |
1079 return; | |
1080 Clipboard clipboard = new Clipboard(getDisplay()); | |
1081 Object[] o = [ stringcast(getSelectionText()) ]; | |
1082 Transfer[] t = [ TextTransfer.getInstance() ]; | |
1083 clipboard.setContents(o, t); | |
1084 clipboard.dispose(); | |
1085 } | |
1086 | |
1087 /** | |
1088 * Returns the reference of the hyperlink that currently has keyboard focus, | |
1089 * or <code>null</code> if there are no hyperlinks in the receiver or no | |
1090 * hyperlink has focus at the moment. | |
1091 * | |
1092 * @return href of the selected hyperlink or <code>null</code> if none | |
1093 * selected. | |
1094 * @since 3.1 | |
1095 */ | |
1096 public Object getSelectedLinkHref() { | |
1097 IHyperlinkSegment link = getSelectedLink(); | |
1098 return link !is null ? stringcast(link.getHref()) : null; | |
1099 } | |
1100 | |
1101 /** | |
1102 * Returns the text of the hyperlink that currently has keyboard focus, or | |
1103 * <code>null</code> if there are no hyperlinks in the receiver or no | |
1104 * hyperlink has focus at the moment. | |
1105 * | |
1106 * @return text of the selected hyperlink or <code>null</code> if none | |
1107 * selected. | |
1108 * @since 3.1 | |
1109 */ | |
1110 public String getSelectedLinkText() { | |
1111 IHyperlinkSegment link = getSelectedLink(); | |
1112 return link !is null ? link.getText() : null; | |
1113 } | |
1114 | |
1115 private IHyperlinkSegment getSelectedLink() { | |
1116 IFocusSelectable segment = model.getSelectedSegment(); | |
1117 if (segment !is null && null !is cast(IHyperlinkSegment)segment ) | |
1118 return cast(IHyperlinkSegment) segment; | |
1119 return null; | |
1120 } | |
1121 | |
1122 private void initAccessible() { | |
1123 Accessible accessible = getAccessible(); | |
1124 accessible.addAccessibleListener(new class AccessibleAdapter { | |
1125 public void getName(AccessibleEvent e) { | |
1126 if (e.childID is ACC.CHILDID_SELF) | |
1127 e.result = model.getAccessibleText(); | |
1128 else { | |
1129 int linkCount = model.getHyperlinkCount(); | |
1130 if (e.childID >= 0 && e.childID < linkCount) { | |
1131 IHyperlinkSegment link = model.getHyperlink(e.childID); | |
1132 e.result = link.getText(); | |
1133 } | |
1134 } | |
1135 } | |
1136 | |
1137 public void getHelp(AccessibleEvent e) { | |
1138 e.result = getToolTipText(); | |
1139 int linkCount = model.getHyperlinkCount(); | |
1140 if (e.result is null && e.childID >= 0 && e.childID < linkCount) { | |
1141 IHyperlinkSegment link = model.getHyperlink(e.childID); | |
1142 e.result = link.getText(); | |
1143 } | |
1144 } | |
1145 }); | |
1146 accessible.addAccessibleControlListener(new class AccessibleControlAdapter { | |
1147 public void getChildAtPoint(AccessibleControlEvent e) { | |
1148 Point pt = toControl(new Point(e.x, e.y)); | |
1149 IHyperlinkSegment link = model.findHyperlinkAt(pt.x, pt.y); | |
1150 if (link !is null) | |
1151 e.childID = model.indexOf(link); | |
1152 else | |
1153 e.childID = ACC.CHILDID_SELF; | |
1154 } | |
1155 | |
1156 public void getLocation(AccessibleControlEvent e) { | |
1157 Rectangle location = null; | |
1158 if (e.childID !is ACC.CHILDID_SELF | |
1159 && e.childID !is ACC.CHILDID_NONE) { | |
1160 int index = e.childID; | |
1161 IHyperlinkSegment link = model.getHyperlink(index); | |
1162 if (link !is null) { | |
1163 location = link.getBounds(); | |
1164 } | |
1165 } | |
1166 if (location is null) { | |
1167 location = getBounds(); | |
1168 } | |
1169 Point pt = toDisplay(new Point(location.x, location.y)); | |
1170 e.x = pt.x; | |
1171 e.y = pt.y; | |
1172 e.width = location.width; | |
1173 e.height = location.height; | |
1174 } | |
1175 | |
1176 public void getFocus(AccessibleControlEvent e) { | |
1177 int childID = ACC.CHILDID_NONE; | |
1178 | |
1179 if (model.hasFocusSegments()) { | |
1180 int selectedIndex = model.getSelectedSegmentIndex(); | |
1181 if (selectedIndex !is -1) { | |
1182 childID = selectedIndex; | |
1183 } | |
1184 } | |
1185 e.childID = childID; | |
1186 } | |
1187 | |
1188 public void getDefaultAction (AccessibleControlEvent e) { | |
1189 if (model.getHyperlinkCount() > 0) { | |
1190 e.result = DWT.getMessage ("SWT_Press"); //$NON-NLS-1$ | |
1191 } | |
1192 } | |
1193 | |
1194 public void getChildCount(AccessibleControlEvent e) { | |
1195 e.detail = model.getHyperlinkCount(); | |
1196 } | |
1197 | |
1198 public void getRole(AccessibleControlEvent e) { | |
1199 int role = 0; | |
1200 int childID = e.childID; | |
1201 int linkCount = model.getHyperlinkCount(); | |
1202 if (childID is ACC.CHILDID_SELF) { | |
1203 if (linkCount > 0) { | |
1204 role = ACC.ROLE_LINK; | |
1205 } else { | |
1206 role = ACC.ROLE_TEXT; | |
1207 } | |
1208 } else if (childID >= 0 && childID < linkCount) { | |
1209 role = ACC.ROLE_LINK; | |
1210 } | |
1211 e.detail = role; | |
1212 } | |
1213 | |
1214 public void getSelection(AccessibleControlEvent e) { | |
1215 int selectedIndex = model.getSelectedSegmentIndex(); | |
1216 e.childID = (selectedIndex is -1) ? ACC.CHILDID_NONE | |
1217 : selectedIndex; | |
1218 } | |
1219 | |
1220 public void getState(AccessibleControlEvent e) { | |
1221 int linkCount = model.getHyperlinkCount(); | |
1222 int selectedIndex = model.getSelectedSegmentIndex(); | |
1223 int state = 0; | |
1224 int childID = e.childID; | |
1225 if (childID is ACC.CHILDID_SELF) { | |
1226 state = ACC.STATE_NORMAL; | |
1227 } else if (childID >= 0 && childID < linkCount) { | |
1228 state = ACC.STATE_SELECTABLE; | |
1229 if (isFocusControl()) { | |
1230 state |= ACC.STATE_FOCUSABLE; | |
1231 } | |
1232 if (selectedIndex is childID) { | |
1233 state |= ACC.STATE_SELECTED; | |
1234 if (isFocusControl()) { | |
1235 state |= ACC.STATE_FOCUSED; | |
1236 } | |
1237 } | |
1238 } | |
1239 state |= ACC.STATE_READONLY; | |
1240 e.detail = state; | |
1241 } | |
1242 | |
1243 public void getChildren(AccessibleControlEvent e) { | |
1244 int linkCount = model.getHyperlinkCount(); | |
1245 Object[] children = new Object[linkCount]; | |
1246 for (int i = 0; i < linkCount; i++) { | |
1247 children[i] = new Integer(i); | |
1248 } | |
1249 e.children = children; | |
1250 } | |
1251 | |
1252 public void getValue(AccessibleControlEvent e) { | |
1253 // e.result = model.getAccessibleText(); | |
1254 } | |
1255 }); | |
1256 } | |
1257 | |
1258 private void startSelection(MouseEvent e) { | |
1259 inSelection = true; | |
1260 selData = new SelectionData(e); | |
1261 redraw(); | |
1262 Form form = FormUtil.getForm(this); | |
1263 if (form !is null) | |
1264 form.setSelectionText(this); | |
1265 } | |
1266 | |
1267 private void endSelection(MouseEvent e) { | |
1268 inSelection = false; | |
1269 if (selData !is null) { | |
1270 if (!selData.isEnclosed()) | |
1271 selData = null; | |
1272 else | |
1273 computeSelection(); | |
1274 } | |
1275 notifySelectionChanged(); | |
1276 } | |
1277 | |
1278 private void computeSelection() { | |
1279 GC gc = new GC(this); | |
1280 Paragraph[] paragraphs = model.getParagraphs(); | |
1281 IHyperlinkSegment selectedLink = getSelectedLink(); | |
1282 if (getDisplay().getFocusControl() !is this) | |
1283 selectedLink = null; | |
1284 for (int i = 0; i < paragraphs.length; i++) { | |
1285 Paragraph p = paragraphs[i]; | |
1286 if (i > 0) | |
1287 selData.markNewLine(); | |
1288 p.computeSelection(gc, resourceTable, selectedLink, selData); | |
1289 } | |
1290 gc.dispose(); | |
1291 } | |
1292 | |
1293 void clearSelection() { | |
1294 selData = null; | |
1295 if (!isDisposed()) { | |
1296 redraw(); | |
1297 notifySelectionChanged(); | |
1298 } | |
1299 } | |
1300 | |
1301 private void notifySelectionChanged() { | |
1302 Event event = new Event(); | |
1303 event.widget = this; | |
1304 event.display = this.getDisplay(); | |
1305 event.type = DWT.Selection; | |
1306 notifyListeners(DWT.Selection, event); | |
1307 getAccessible().selectionChanged(); | |
1308 } | |
1309 | |
1310 private void handleDrag(MouseEvent e) { | |
1311 if (selData !is null) { | |
1312 ScrolledComposite scomp = FormUtil.getScrolledComposite(this); | |
1313 if (scomp !is null) { | |
1314 FormUtil.ensureVisible(scomp, this, e); | |
1315 } | |
1316 selData.update(e); | |
1317 redraw(); | |
1318 } | |
1319 } | |
1320 | |
1321 private void handleMouseClick(MouseEvent e, bool down) { | |
1322 if (DEBUG_FOCUS) | |
1323 Stdout.formatln("FormText: mouse click({})", down ); //$NON-NLS-1$ //$NON-NLS-2$ | |
1324 if (down) { | |
1325 // select a hyperlink | |
1326 mouseFocus = true; | |
1327 IHyperlinkSegment segmentUnder = model.findHyperlinkAt(e.x, e.y); | |
1328 if (segmentUnder !is null) { | |
1329 IHyperlinkSegment oldLink = getSelectedLink(); | |
1330 if (getDisplay().getFocusControl() !is this) { | |
1331 setFocus(); | |
1332 } | |
1333 model.selectLink(segmentUnder); | |
1334 enterLink(segmentUnder, e.stateMask); | |
1335 paintFocusTransfer(oldLink, segmentUnder); | |
1336 } | |
1337 if (e.button is 1) { | |
1338 startSelection(e); | |
1339 armed = segmentUnder; | |
1340 } | |
1341 else { | |
1342 } | |
1343 } else { | |
1344 if (e.button is 1) { | |
1345 endSelection(e); | |
1346 IHyperlinkSegment segmentUnder = model | |
1347 .findHyperlinkAt(e.x, e.y); | |
1348 if (segmentUnder !is null && armed is segmentUnder && selData is null) { | |
1349 activateLink(segmentUnder, e.stateMask); | |
1350 armed = null; | |
1351 } | |
1352 } | |
1353 mouseFocus = false; | |
1354 } | |
1355 } | |
1356 | |
1357 private void handleMouseHover(MouseEvent e) { | |
1358 } | |
1359 | |
1360 private void updateTooltipText(ParagraphSegment segment) { | |
1361 String tooltipText = null; | |
1362 if (segment !is null) { | |
1363 tooltipText = segment.getTooltipText(); | |
1364 } | |
1365 String currentTooltipText = getToolTipText(); | |
1366 | |
1367 if ((currentTooltipText !is null && tooltipText is null) | |
1368 || (currentTooltipText is null && tooltipText !is null)) | |
1369 setToolTipText(tooltipText); | |
1370 } | |
1371 | |
1372 private void handleMouseMove(MouseEvent e) { | |
1373 if (inSelection) { | |
1374 handleDrag(e); | |
1375 return; | |
1376 } | |
1377 ParagraphSegment segmentUnder = model.findSegmentAt(e.x, e.y); | |
1378 updateTooltipText(segmentUnder); | |
1379 if (segmentUnder is null) { | |
1380 if (entered !is null) { | |
1381 exitLink(entered, e.stateMask); | |
1382 paintLinkHover(entered, false); | |
1383 entered = null; | |
1384 } | |
1385 setCursor(null); | |
1386 } else { | |
1387 if (auto linkUnder = cast(IHyperlinkSegment) segmentUnder ) { | |
1388 if (entered !is null && linkUnder !is entered) { | |
1389 // Special case: links are so close that there are 0 pixels between. | |
1390 // Must exit the link before entering the next one. | |
1391 exitLink(entered, e.stateMask); | |
1392 paintLinkHover(entered, false); | |
1393 entered = null; | |
1394 } | |
1395 if (entered is null) { | |
1396 entered = linkUnder; | |
1397 enterLink(linkUnder, e.stateMask); | |
1398 paintLinkHover(entered, true); | |
1399 setCursor(model.getHyperlinkSettings().getHyperlinkCursor()); | |
1400 } | |
1401 } else { | |
1402 if (entered !is null) { | |
1403 exitLink(entered, e.stateMask); | |
1404 paintLinkHover(entered, false); | |
1405 entered = null; | |
1406 } | |
1407 if (null !is cast(TextSegment)segmentUnder ) | |
1408 setCursor(model.getHyperlinkSettings().getTextCursor()); | |
1409 else | |
1410 setCursor(null); | |
1411 } | |
1412 } | |
1413 } | |
1414 | |
1415 private bool advance(bool next) { | |
1416 if (DEBUG_FOCUS) | |
1417 Stdout.formatln("Advance: next={}", next); //$NON-NLS-1$ | |
1418 IFocusSelectable current = model.getSelectedSegment(); | |
1419 if (current !is null && null !is cast(IHyperlinkSegment)current ) | |
1420 exitLink(cast(IHyperlinkSegment) current, DWT.NULL); | |
1421 IFocusSelectable newSegment = null; | |
1422 bool valid = false; | |
1423 // get the next segment that can accept focus. Links | |
1424 // can always accept focus but controls may not | |
1425 while (!valid) { | |
1426 if (!model.traverseFocusSelectableObjects(next)) | |
1427 break; | |
1428 newSegment = model.getSelectedSegment(); | |
1429 if (newSegment is null) | |
1430 break; | |
1431 valid = setControlFocus(next, newSegment); | |
1432 } | |
1433 IHyperlinkSegment newLink = null !is cast(IHyperlinkSegment)newSegment ? cast(IHyperlinkSegment) newSegment | |
1434 : null; | |
1435 if (valid) | |
1436 enterLink(newLink, DWT.NULL); | |
1437 IHyperlinkSegment oldLink = null !is cast(IHyperlinkSegment)current ? cast(IHyperlinkSegment) current | |
1438 : null; | |
1439 if (oldLink !is null || newLink !is null) | |
1440 paintFocusTransfer(oldLink, newLink); | |
1441 if (newLink !is null) | |
1442 ensureVisible(newLink); | |
1443 if (newLink !is null) | |
1444 getAccessible().setFocus(model.getSelectedSegmentIndex()); | |
1445 return !valid; | |
1446 } | |
1447 | |
1448 private bool setControlFocus(bool next, IFocusSelectable selectable) { | |
1449 controlFocusTransfer = true; | |
1450 bool result = selectable.setFocus(resourceTable, next); | |
1451 controlFocusTransfer = false; | |
1452 return result; | |
1453 } | |
1454 | |
1455 private void handleFocusChange() { | |
1456 if (DEBUG_FOCUS) { | |
1457 Stdout.formatln("Handle focus change: hasFocus={}, mouseFocus={}", hasFocus, //$NON-NLS-1$ | |
1458 mouseFocus); //$NON-NLS-1$ | |
1459 } | |
1460 if (hasFocus) { | |
1461 bool advance = true; | |
1462 if (!mouseFocus) { | |
1463 // if (model.restoreSavedLink() is false) | |
1464 bool valid = false; | |
1465 IFocusSelectable selectable = null; | |
1466 while (!valid) { | |
1467 if (!model.traverseFocusSelectableObjects(advance)) | |
1468 break; | |
1469 selectable = model.getSelectedSegment(); | |
1470 if (selectable is null) | |
1471 break; | |
1472 valid = setControlFocus(advance, selectable); | |
1473 } | |
1474 if (selectable is null) | |
1475 setFocusToNextSibling(this, true); | |
1476 else | |
1477 ensureVisible(selectable); | |
1478 if ( auto hls = cast(IHyperlinkSegment)selectable ) { | |
1479 enterLink(hls, DWT.NULL); | |
1480 paintFocusTransfer(null, hls); | |
1481 } | |
1482 } | |
1483 } else { | |
1484 paintFocusTransfer(getSelectedLink(), null); | |
1485 model.selectLink(null); | |
1486 } | |
1487 } | |
1488 | |
1489 private void enterLink(IHyperlinkSegment link, int stateMask) { | |
1490 if (link is null || listeners is null) | |
1491 return; | |
1492 int size = listeners.size(); | |
1493 HyperlinkEvent he = new HyperlinkEvent(this, stringcast(link.getHref()), link | |
1494 .getText(), stateMask); | |
1495 Object [] listenerList = listeners.getListeners(); | |
1496 for (int i = 0; i < size; i++) { | |
1497 IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i]; | |
1498 listener.linkEntered(he); | |
1499 } | |
1500 } | |
1501 | |
1502 private void exitLink(IHyperlinkSegment link, int stateMask) { | |
1503 if (link is null || listeners is null) | |
1504 return; | |
1505 int size = listeners.size(); | |
1506 HyperlinkEvent he = new HyperlinkEvent(this, stringcast(link.getHref()), link | |
1507 .getText(), stateMask); | |
1508 Object [] listenerList = listeners.getListeners(); | |
1509 for (int i = 0; i < size; i++) { | |
1510 IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i]; | |
1511 listener.linkExited(he); | |
1512 } | |
1513 } | |
1514 | |
1515 private void paintLinkHover(IHyperlinkSegment link, bool hover) { | |
1516 GC gc = new GC(this); | |
1517 HyperlinkSettings settings = getHyperlinkSettings(); | |
1518 Color newFg = hover ? settings.getActiveForeground() : settings | |
1519 .getForeground(); | |
1520 if (newFg !is null) | |
1521 gc.setForeground(newFg); | |
1522 gc.setBackground(getBackground()); | |
1523 gc.setFont(getFont()); | |
1524 bool selected = (link is getSelectedLink()); | |
1525 (cast(ParagraphSegment) link).paint(gc, hover, resourceTable, selected, | |
1526 selData, null); | |
1527 gc.dispose(); | |
1528 } | |
1529 | |
1530 private void activateSelectedLink() { | |
1531 IHyperlinkSegment link = getSelectedLink(); | |
1532 if (link !is null) | |
1533 activateLink(link, DWT.NULL); | |
1534 } | |
1535 | |
1536 private void activateLink(IHyperlinkSegment link, int stateMask) { | |
1537 setCursor(model.getHyperlinkSettings().getBusyCursor()); | |
1538 if (listeners !is null) { | |
1539 int size = listeners.size(); | |
1540 HyperlinkEvent e = new HyperlinkEvent(this, stringcast(link.getHref()), link | |
1541 .getText(), stateMask); | |
1542 Object [] listenerList = listeners.getListeners(); | |
1543 for (int i = 0; i < size; i++) { | |
1544 IHyperlinkListener listener = cast(IHyperlinkListener) listenerList[i]; | |
1545 listener.linkActivated(e); | |
1546 } | |
1547 } | |
1548 if (!isDisposed() && model.linkExists(link)) { | |
1549 setCursor(model.getHyperlinkSettings().getHyperlinkCursor()); | |
1550 } | |
1551 } | |
1552 | |
1553 private void ensureBoldFontPresent(Font regularFont) { | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1554 Font boldFont = cast(Font) resourceTable.get(FormTextModel.BOLD_FONT_ID); |
75 | 1555 if (boldFont !is null) |
1556 return; | |
1557 boldFont = FormFonts.getInstance().getBoldFont(getDisplay(), regularFont); | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1558 resourceTable.put(FormTextModel.BOLD_FONT_ID, boldFont); |
75 | 1559 } |
1560 | |
1561 private void paint(PaintEvent e) { | |
1562 GC gc = e.gc; | |
1563 gc.setFont(getFont()); | |
1564 ensureBoldFontPresent(getFont()); | |
1565 gc.setForeground(getForeground()); | |
1566 gc.setBackground(getBackground()); | |
1567 repaint(gc, e.x, e.y, e.width, e.height); | |
1568 } | |
1569 | |
1570 private void repaint(GC gc, int x, int y, int width, int height) { | |
1571 Image textBuffer = new Image(getDisplay(), width, height); | |
1572 Color bg = getBackground(); | |
1573 Color fg = getForeground(); | |
1574 if (!getEnabled()) { | |
1575 bg = getDisplay().getSystemColor(DWT.COLOR_WIDGET_BACKGROUND); | |
1576 fg = getDisplay().getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW); | |
1577 } | |
1578 GC textGC = new GC(textBuffer, gc.getStyle()); | |
1579 textGC.setForeground(fg); | |
1580 textGC.setBackground(bg); | |
1581 textGC.setFont(getFont()); | |
1582 textGC.fillRectangle(0, 0, width, height); | |
1583 Rectangle repaintRegion = new Rectangle(x, y, width, height); | |
1584 | |
1585 Paragraph[] paragraphs = model.getParagraphs(); | |
1586 IHyperlinkSegment selectedLink = getSelectedLink(); | |
1587 if (getDisplay().getFocusControl() !is this) | |
1588 selectedLink = null; | |
1589 for (int i = 0; i < paragraphs.length; i++) { | |
1590 Paragraph p = paragraphs[i]; | |
1591 p | |
1592 .paint(textGC, repaintRegion, resourceTable, selectedLink, | |
1593 selData); | |
1594 } | |
1595 textGC.dispose(); | |
1596 gc.drawImage(textBuffer, x, y); | |
1597 textBuffer.dispose(); | |
1598 } | |
1599 | |
1600 private int getParagraphSpacing(int lineHeight) { | |
1601 return lineHeight / 2; | |
1602 } | |
1603 | |
1604 private void paintFocusTransfer(IHyperlinkSegment oldLink, | |
1605 IHyperlinkSegment newLink) { | |
1606 GC gc = new GC(this); | |
1607 Color bg = getBackground(); | |
1608 Color fg = getForeground(); | |
1609 gc.setFont(getFont()); | |
1610 if (oldLink !is null) { | |
1611 gc.setBackground(bg); | |
1612 gc.setForeground(fg); | |
1613 oldLink.paintFocus(gc, bg, fg, false, null); | |
1614 } | |
1615 if (newLink !is null) { | |
1616 // ensureVisible(newLink); | |
1617 gc.setBackground(bg); | |
1618 gc.setForeground(fg); | |
1619 newLink.paintFocus(gc, bg, fg, true, null); | |
1620 } | |
1621 gc.dispose(); | |
1622 } | |
1623 | |
1624 private void ensureVisible(IFocusSelectable segment) { | |
1625 if (mouseFocus) { | |
1626 mouseFocus = false; | |
1627 return; | |
1628 } | |
1629 if (segment is null) | |
1630 return; | |
1631 Rectangle bounds = segment.getBounds(); | |
1632 ScrolledComposite scomp = FormUtil.getScrolledComposite(this); | |
1633 if (scomp is null) | |
1634 return; | |
1635 Point origin = FormUtil.getControlLocation(scomp, this); | |
1636 origin.x += bounds.x; | |
1637 origin.y += bounds.y; | |
1638 FormUtil.ensureVisible(scomp, origin, new Point(bounds.width, | |
1639 bounds.height)); | |
1640 } | |
1641 | |
1642 /** | |
1643 * Overrides the method by fully trusting the layout manager (computed width | |
1644 * or height may be larger than the provider width or height hints). Callers | |
1645 * should be prepared that the computed width is larger than the provided | |
1646 * wHint. | |
1647 * | |
1648 * @see dwt.widgets.Composite#computeSize(int, int, bool) | |
1649 */ | |
1650 public Point computeSize(int wHint, int hHint, bool changed) { | |
1651 checkWidget(); | |
1652 Point size; | |
1653 FormTextLayout layout = cast(FormTextLayout) getLayout(); | |
1654 if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) { | |
1655 size = layout.computeSize(this, wHint, hHint, changed); | |
1656 } else { | |
1657 size = new Point(wHint, hHint); | |
1658 } | |
1659 Rectangle trim = computeTrim(0, 0, size.x, size.y); | |
1660 if (DEBUG_TEXTSIZE) | |
1661 Stdout.formatln("FormText Computed size: {}",trim); //$NON-NLS-1$ | |
1662 return new Point(trim.width, trim.height); | |
1663 } | |
1664 | |
1665 private void disposeResourceTable(bool disposeBoldFont) { | |
1666 if (disposeBoldFont) { | |
1667 Font boldFont = cast(Font) resourceTable | |
1668 .get(FormTextModel.BOLD_FONT_ID); | |
1669 if (boldFont !is null) { | |
1670 FormFonts.getInstance().markFinished(boldFont); | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1671 resourceTable.remove(FormTextModel.BOLD_FONT_ID); |
75 | 1672 } |
1673 } | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1674 ArrayList imagesToRemove = new ArrayList(); |
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1675 for (Enumeration enm = resourceTable.keys(); enm.hasMoreElements();) { |
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1676 String key = stringcast( enm.nextElement()); |
75 | 1677 if (key.startsWith(ImageSegment.SEL_IMAGE_PREFIX)) { |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1678 Object obj = resourceTable.get(key); |
75 | 1679 if (auto image = cast(Image)obj ) { |
1680 if (!image.isDisposed()) { | |
1681 image.dispose(); | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1682 imagesToRemove.add(key); |
75 | 1683 } |
1684 } | |
1685 } | |
1686 } | |
1687 for (int i = 0; i < imagesToRemove.size(); i++) { | |
104
04b47443bb01
Reworked the collection uses to make use of a wrapper collection that is compatible to the Java Collections.
Frank Benoit <benoit@tionex.de>
parents:
85
diff
changeset
|
1688 resourceTable.remove(imagesToRemove.get(i)); |
75 | 1689 } |
1690 } | |
1691 | |
1692 /* | |
1693 * (non-Javadoc) | |
1694 * | |
1695 * @see dwt.widgets.Control#setEnabled(bool) | |
1696 */ | |
1697 public void setEnabled(bool enabled) { | |
1698 super.setEnabled(enabled); | |
1699 redraw(); | |
1700 } | |
1701 | |
1702 /* (non-Javadoc) | |
1703 * @see dwt.widgets.Control#setFocus() | |
1704 */ | |
1705 public bool setFocus() { | |
1706 FormUtil.setFocusScrollingEnabled(this, false); | |
1707 bool result = super.setFocus(); | |
1708 FormUtil.setFocusScrollingEnabled(this, true); | |
1709 return result; | |
1710 } | |
1711 } |