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 16 import org.eclipse.swt.SWT; 17 import org.eclipse.swt.SWTException; 18 import org.eclipse.swt.accessibility.ACC; 19 import org.eclipse.swt.accessibility.Accessible; 20 import org.eclipse.swt.accessibility.AccessibleAdapter; 21 import org.eclipse.swt.accessibility.AccessibleControlAdapter; 22 import org.eclipse.swt.accessibility.AccessibleControlEvent; 23 import org.eclipse.swt.accessibility.AccessibleEvent; 24 import org.eclipse.swt.events.SelectionEvent; 25 import org.eclipse.swt.events.SelectionListener; 26 import org.eclipse.swt.graphics.Color; 27 import org.eclipse.swt.graphics.Font; 28 import org.eclipse.swt.graphics.GC; 29 import org.eclipse.swt.graphics.GCData; 30 import org.eclipse.swt.graphics.Point; 31 import org.eclipse.swt.graphics.RGB; 32 import org.eclipse.swt.graphics.Rectangle; 33 import org.eclipse.swt.graphics.TextLayout; 34 import org.eclipse.swt.graphics.TextStyle; 35 import org.eclipse.swt.internal.gtk.OS; 36 37 import org.eclipse.swt.graphics.Cursor; 38 import org.eclipse.swt.widgets.Control; 39 import org.eclipse.swt.widgets.Composite; 40 import org.eclipse.swt.widgets.TypedListener; 41 import org.eclipse.swt.widgets.Event; 42 import java.lang.all; 43 import java.nonstandard.UnsafeUtf; 44 45 /** 46 * Instances of this class represent a selectable 47 * user interface object that displays a text with 48 * links. 49 * <p> 50 * <dl> 51 * <dt><b>Styles:</b></dt> 52 * <dd>(none)</dd> 53 * <dt><b>Events:</b></dt> 54 * <dd>Selection</dd> 55 * </dl> 56 * <p> 57 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 58 * </p> 59 * 60 * @see <a href="http://www.eclipse.org/swt/snippets/#link">Link snippets</a> 61 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 62 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 63 * 64 * @since 3.1 65 */ 66 public class Link : Control { 67 68 alias Control.computeSize computeSize; 69 alias Control.fixStyle fixStyle; 70 alias Control.setBounds setBounds; 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; 80 81 static RGB LINK_FOREGROUND; 82 static RGB LINK_DISABLED_FOREGROUND; 83 84 static void static_this(){ 85 if( LINK_FOREGROUND is null ){ 86 LINK_FOREGROUND = new RGB (0, 51, 153); 87 } 88 if( LINK_DISABLED_FOREGROUND is null ){ 89 LINK_DISABLED_FOREGROUND = new RGB (172, 168, 153); 90 } 91 } 92 93 /** 94 * Constructs a new instance of this class given its parent 95 * and a style value describing its behavior and appearance. 96 * <p> 97 * The style value is either one of the style constants defined in 98 * class <code>SWT</code> which is applicable to instances of this 99 * class, or must be built by <em>bitwise OR</em>'ing together 100 * (that is, using the <code>int</code> "|" operator) two or more 101 * of those <code>SWT</code> style constants. The class description 102 * lists the style constants that are applicable to the class. 103 * Style bits are also inherited from superclasses. 104 * </p> 105 * 106 * @param parent a composite control which will be the parent of the new instance (cannot be null) 107 * @param style the style of control to construct 108 * 109 * @exception IllegalArgumentException <ul> 110 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 111 * </ul> 112 * @exception SWTException <ul> 113 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 114 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 115 * </ul> 116 * 117 * @see Widget#checkSubclass 118 * @see Widget#getStyle 119 */ 120 public this (Composite parent, int style) { 121 static_this(); 122 super (parent, style); 123 } 124 125 /** 126 * Adds the listener to the collection of listeners who will 127 * be notified when the control is selected by the user, by sending 128 * it one of the messages defined in the <code>SelectionListener</code> 129 * interface. 130 * <p> 131 * <code>widgetSelected</code> is called when the control is selected by the user. 132 * <code>widgetDefaultSelected</code> is not called. 133 * </p> 134 * 135 * @param listener the listener which should be notified 136 * 137 * @exception IllegalArgumentException <ul> 138 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 139 * </ul> 140 * @exception SWTException <ul> 141 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 142 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 143 * </ul> 144 * 145 * @see SelectionListener 146 * @see #removeSelectionListener 147 * @see SelectionEvent 148 */ 149 public void addSelectionListener (SelectionListener listener) { 150 checkWidget (); 151 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 152 TypedListener typedListener = new TypedListener (listener); 153 addListener (SWT.Selection, typedListener); 154 addListener (SWT.DefaultSelection, typedListener); 155 } 156 157 public override Point computeSize (int wHint, int hHint, bool changed) { 158 checkWidget (); 159 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 160 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 161 int width, height; 162 int layoutWidth = layout.getWidth (); 163 //TEMPORARY CODE 164 if (wHint is 0) { 165 layout.setWidth (1); 166 Rectangle rect = layout.getBounds (); 167 width = 0; 168 height = rect.height; 169 } else { 170 layout.setWidth (wHint); 171 Rectangle rect = layout.getBounds (); 172 width = rect.width; 173 height = rect.height; 174 } 175 layout.setWidth (layoutWidth); 176 if (wHint !is SWT.DEFAULT) width = wHint; 177 if (hHint !is SWT.DEFAULT) height = hHint; 178 int border = getBorderWidth (); 179 width += border * 2; 180 height += border * 2; 181 return new Point (width, height); 182 } 183 184 override void createHandle(int index) { 185 state |= HANDLE | THEME_BACKGROUND; 186 handle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 187 if (handle is null) SWT.error (SWT.ERROR_NO_HANDLES); 188 OS.gtk_fixed_set_has_window (handle, true); 189 OS.GTK_WIDGET_SET_FLAGS (handle, OS.GTK_CAN_FOCUS); 190 layout = new TextLayout (display); 191 layout.setOrientation((style & SWT.RIGHT_TO_LEFT) !is 0? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT); 192 linkColor = new Color (display, LINK_FOREGROUND); 193 disabledColor = new Color (display, LINK_DISABLED_FOREGROUND); 194 offsets = null; 195 ids = null; 196 mnemonics = null; 197 selection = new Point (-1, -1); 198 focusIndex = -1; 199 } 200 201 override void createWidget (int index) { 202 super.createWidget (index); 203 layout.setFont (getFont ()); 204 text = ""; 205 initAccessible (); 206 } 207 208 override void enableWidget (bool enabled) { 209 super.enableWidget (enabled); 210 if (isDisposed ()) return; 211 TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null); 212 linkStyle.underline = true; 213 for (int i = 0; i < offsets.length; i++) { 214 Point point = offsets [i]; 215 layout.setStyle (linkStyle, point.x, point.y); 216 } 217 redraw (); 218 } 219 220 override void fixStyle () { 221 fixStyle (handle); 222 } 223 224 void initAccessible () { 225 Accessible accessible = getAccessible (); 226 accessible.addAccessibleListener (new class () AccessibleAdapter { 227 override 228 public void getName (AccessibleEvent e) { 229 e.result = parse (text); 230 } 231 }); 232 233 accessible.addAccessibleControlListener (new class () AccessibleControlAdapter { 234 override 235 public void getChildAtPoint (AccessibleControlEvent e) { 236 e.childID = ACC.CHILDID_SELF; 237 } 238 239 override 240 public void getLocation (AccessibleControlEvent e) { 241 Rectangle rect = display.map (getParent (), null, getBounds ()); 242 e.x = rect.x; 243 e.y = rect.y; 244 e.width = rect.width; 245 e.height = rect.height; 246 } 247 248 override 249 public void getChildCount (AccessibleControlEvent e) { 250 e.detail = 0; 251 } 252 253 override 254 public void getRole (AccessibleControlEvent e) { 255 e.detail = ACC.ROLE_LINK; 256 } 257 258 override 259 public void getState (AccessibleControlEvent e) { 260 e.detail = ACC.STATE_FOCUSABLE; 261 if (hasFocus ()) e.detail |= ACC.STATE_FOCUSED; 262 } 263 264 override 265 public void getDefaultAction (AccessibleControlEvent e) { 266 e.result = SWT.getMessage ("SWT_Press"); //$NON-NLS-1$ 267 } 268 269 override 270 public void getSelection (AccessibleControlEvent e) { 271 if (hasFocus ()) e.childID = ACC.CHILDID_SELF; 272 } 273 274 override 275 public void getFocus (AccessibleControlEvent e) { 276 if (hasFocus ()) e.childID = ACC.CHILDID_SELF; 277 } 278 }); 279 } 280 281 override String getNameText () { 282 return getText (); 283 } 284 285 Rectangle [] getRectangles (int linkIndex) { 286 int lineCount = layout.getLineCount (); 287 Rectangle [] rects = new Rectangle [lineCount]; 288 int [] lineOffsets = layout.getLineOffsets (); 289 Point point = offsets [linkIndex]; 290 int lineStart = 1; 291 while (point.x > lineOffsets [lineStart]) lineStart++; 292 int lineEnd = 1; 293 while (point.y > lineOffsets [lineEnd]) lineEnd++; 294 int index = 0; 295 if (lineStart is lineEnd) { 296 rects [index++] = layout.getBounds (point.x, point.y); 297 } else { 298 rects [index++] = layout.getBounds (point.x, lineOffsets [lineStart]-1); 299 rects [index++] = layout.getBounds (lineOffsets [lineEnd-1], point.y); 300 if (lineEnd - lineStart > 1) { 301 for (int i = lineStart; i < lineEnd - 1; i++) { 302 rects [index++] = layout.getLineBounds (i); 303 } 304 } 305 } 306 if (rects.length !is index) { 307 Rectangle [] tmp = new Rectangle [index]; 308 System.arraycopy (rects, 0, tmp, 0, index); 309 rects = tmp; 310 } 311 return rects; 312 } 313 314 override 315 int getClientWidth () { 316 return (state & ZERO_WIDTH) !is 0 ? 0 : OS.GTK_WIDGET_WIDTH (handle); 317 } 318 319 /** 320 * Returns the receiver's text, which will be an empty 321 * string if it has never been set. 322 * 323 * @return the receiver's text 324 * 325 * @exception SWTException <ul> 326 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 327 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 328 * </ul> 329 */ 330 public String getText () { 331 checkWidget (); 332 return text; 333 } 334 335 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 336 int result = super.gtk_button_press_event (widget, gdkEvent); 337 if (result !is 0) return result; 338 if (gdkEvent.button is 1 && gdkEvent.type is OS.GDK_BUTTON_PRESS) { 339 if (focusIndex !is -1) setFocus (); 340 int x = cast(int) gdkEvent.x; 341 int y = cast(int) gdkEvent.y; 342 if ((style & SWT.MIRRORED) !is 0) x = getClientWidth () - x; 343 int offset = layout.getOffset (x, y, null); 344 int oldSelectionX = selection.x; 345 int oldSelectionY = selection.y; 346 selection.x = offset; 347 selection.y = -1; 348 if (oldSelectionX !is -1 && oldSelectionY !is -1) { 349 if (oldSelectionX > oldSelectionY) { 350 int temp = oldSelectionX; 351 oldSelectionX = oldSelectionY; 352 oldSelectionY = temp; 353 } 354 Rectangle rect = layout.getBounds (oldSelectionX, oldSelectionY); 355 redraw (rect.x, rect.y, rect.width, rect.height, false); 356 } 357 for (int j = 0; j < offsets.length; j++) { 358 Rectangle [] rects = getRectangles (j); 359 for (int i = 0; i < rects.length; i++) { 360 Rectangle rect = rects [i]; 361 if (rect.contains (x, y)) { 362 focusIndex = j; 363 redraw (); 364 return result; 365 } 366 } 367 } 368 } 369 return result; 370 } 371 372 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 373 int result = super.gtk_button_release_event (widget, gdkEvent); 374 if (result !is 0) return result; 375 if (focusIndex is -1) return result; 376 if (gdkEvent.button is 1) { 377 int x = cast(int) gdkEvent.x; 378 int y = cast(int) gdkEvent.y; 379 if ((style & SWT.MIRRORED) !is 0) x = getClientWidth () - x; 380 Rectangle [] rects = getRectangles (focusIndex); 381 for (int i = 0; i < rects.length; i++) { 382 Rectangle rect = rects [i]; 383 if (rect.contains (x, y)) { 384 Event ev = new Event (); 385 ev.text = ids [focusIndex]; 386 sendEvent (SWT.Selection, ev); 387 return result; 388 } 389 } 390 } 391 return result; 392 } 393 394 override int gtk_event_after (GtkWidget* widget, GdkEvent* event) { 395 int result = super.gtk_event_after (widget, event); 396 switch (event.type) { 397 case OS.GDK_FOCUS_CHANGE: 398 redraw (); 399 break; 400 default: 401 } 402 return result; 403 } 404 405 override int gtk_expose_event (GtkWidget* widget, GdkEventExpose* gdkEvent) { 406 if ((state & OBSCURED) !is 0) return 0; 407 GCData data = new GCData (); 408 data.damageRgn = gdkEvent.region; 409 GC gc = GC.gtk_new (this, data); 410 OS.gdk_gc_set_clip_region (gc.handle, gdkEvent.region); 411 int selStart = selection.x; 412 int selEnd = selection.y; 413 if (selStart > selEnd) { 414 selStart = selection.y; 415 selEnd = selection.x; 416 } 417 // temporary code to disable text selection 418 selStart = selEnd = -1; 419 if ((state & DISABLED) !is 0) gc.setForeground (disabledColor); 420 layout.draw (gc, 0, 0, selStart, selEnd, null, null); 421 if (hasFocus () && focusIndex !is -1) { 422 Rectangle [] rects = getRectangles (focusIndex); 423 for (int i = 0; i < rects.length; i++) { 424 Rectangle rect = rects [i]; 425 gc.drawFocus (rect.x, rect.y, rect.width, rect.height); 426 } 427 } 428 if (hooks (SWT.Paint) || filters (SWT.Paint)) { 429 Event event = new Event (); 430 event.count = gdkEvent.count; 431 event.x = gdkEvent.area.x; 432 event.y = gdkEvent.area.y; 433 event.width = gdkEvent.area.width; 434 event.height = gdkEvent.area.height; 435 if ((style & SWT.MIRRORED) !is 0) event.x = getClientWidth () - event.width - event.x; 436 event.gc = gc; 437 sendEvent (SWT.Paint, event); 438 event.gc = null; 439 } 440 gc.dispose (); 441 return 0; 442 } 443 444 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* gdkEvent) { 445 int result = super.gtk_key_press_event (widget, gdkEvent); 446 if (result !is 0) return result; 447 if (focusIndex is -1) return result; 448 switch (gdkEvent.keyval) { 449 case OS.GDK_Return: 450 case OS.GDK_KP_Enter: 451 case OS.GDK_space: 452 Event event = new Event (); 453 event.text = ids [focusIndex]; 454 sendEvent (SWT.Selection, event); 455 break; 456 case OS.GDK_Tab: 457 if (focusIndex < offsets.length - 1) { 458 focusIndex++; 459 redraw (); 460 } 461 break; 462 case OS.GDK_ISO_Left_Tab: 463 if (focusIndex > 0) { 464 focusIndex--; 465 redraw (); 466 } 467 break; 468 default: 469 } 470 return result; 471 } 472 473 override int gtk_motion_notify_event (GtkWidget* widget, GdkEventMotion* gdkEvent) { 474 int result = super.gtk_motion_notify_event (widget, gdkEvent); 475 if (result !is 0) return result; 476 int x = cast(int) gdkEvent.x; 477 int y = cast(int) gdkEvent.y; 478 if ((style & SWT.MIRRORED) !is 0) x = getClientWidth () - x; 479 if ((gdkEvent.state & OS.GDK_BUTTON1_MASK) !is 0) { 480 int oldSelection = selection.y; 481 selection.y = layout.getOffset (x, y, null); 482 if (selection.y !is oldSelection) { 483 int newSelection = selection.y; 484 if (oldSelection > newSelection) { 485 int temp = oldSelection; 486 oldSelection = newSelection; 487 newSelection = temp; 488 } 489 Rectangle rect = layout.getBounds (oldSelection, newSelection); 490 redraw (rect.x, rect.y, rect.width, rect.height, false); 491 } 492 } else { 493 for (int j = 0; j < offsets.length; j++) { 494 Rectangle [] rects = getRectangles (j); 495 for (int i = 0; i < rects.length; i++) { 496 Rectangle rect = rects [i]; 497 if (rect.contains (x, y)) { 498 setCursor (display.getSystemCursor (SWT.CURSOR_HAND)); 499 return result; 500 } 501 } 502 } 503 setCursor (null); 504 } 505 return result; 506 } 507 508 override void releaseWidget () { 509 super.releaseWidget (); 510 if (layout !is null) layout.dispose (); 511 layout = null; 512 if (linkColor !is null) linkColor.dispose (); 513 linkColor = null; 514 if (disabledColor !is null) disabledColor.dispose (); 515 disabledColor = null; 516 offsets = null; 517 ids = null; 518 mnemonics = null; 519 text = null; 520 } 521 522 /** 523 * Removes the listener from the collection of listeners who will 524 * be notified when the control is selected by the user. 525 * 526 * @param listener the listener which should no longer be notified 527 * 528 * @exception IllegalArgumentException <ul> 529 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 530 * </ul> 531 * @exception SWTException <ul> 532 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 533 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 534 * </ul> 535 * 536 * @see SelectionListener 537 * @see #addSelectionListener 538 */ 539 public void removeSelectionListener (SelectionListener listener) { 540 checkWidget (); 541 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 542 if (eventTable is null) return; 543 eventTable.unhook (SWT.Selection, listener); 544 eventTable.unhook (SWT.DefaultSelection, listener); 545 } 546 547 String parse (String string) { 548 ptrdiff_t length_ = string.length; 549 offsets = new Point[]( length_ / 4 ); 550 ids = new String[]( length_ / 4 ); 551 mnemonics = new int[] ( length_ / 4 + 1 ); 552 StringBuffer result = new StringBuffer (); 553 char [] buffer = string.dup; 554 555 int index = 0, state = 0, linkIndex = 0; 556 int start = 0, tagStart = 0, linkStart = 0, endtagStart = 0, refStart = 0; 557 while (index < length_) { 558 ptrdiff_t increment; 559 dchar c = Character.toLowerCase (buffer.dcharAt (index, increment)); 560 561 switch (state) { 562 case 0: 563 if (c is '<') { 564 tagStart = index; 565 state++; 566 } 567 break; 568 case 1: 569 if (c is 'a') state++; 570 break; 571 case 2: 572 switch (c) { 573 case 'h': 574 state = 7; 575 break; 576 case '>': 577 linkStart = index + 1; 578 state++; 579 break; 580 default: 581 if (Character.isWhitespace(c)) break; 582 else state = 13; 583 } 584 break; 585 case 3: 586 if (c is '<') { 587 endtagStart = index; 588 state++; 589 } 590 break; 591 case 4: 592 state = c is '/' ? state + 1 : 3; 593 break; 594 case 5: 595 state = c is 'a' ? state + 1 : 3; 596 break; 597 case 6: 598 if (c is '>') { 599 mnemonics [linkIndex] = parseMnemonics (buffer, start, tagStart, result); 600 int offset = result.length (); 601 parseMnemonics (buffer, linkStart, endtagStart, result); 602 offsets [linkIndex] = new Point (offset, result.length () - 1); 603 if (ids [linkIndex] is null) { 604 ids [linkIndex] = buffer[ linkStart .. endtagStart ]._idup(); 605 } 606 linkIndex++; 607 start = tagStart = linkStart = endtagStart = refStart = index + 1; 608 state = 0; 609 } else { 610 state = 3; 611 } 612 break; 613 case 7: 614 state = c is 'r' ? state + 1 : 0; 615 break; 616 case 8: 617 state = c is 'e' ? state + 1 : 0; 618 break; 619 case 9: 620 state = c is 'f' ? state + 1 : 0; 621 break; 622 case 10: 623 state = c is '=' ? state + 1 : 0; 624 break; 625 case 11: 626 if (c is '"') { 627 state++; 628 refStart = index + 1; 629 } else { 630 state = 0; 631 } 632 break; 633 case 12: 634 if (c is '"') { 635 ids[linkIndex] = buffer[ refStart .. index ]._idup(); 636 state = 2; 637 } 638 break; 639 case 13: 640 if (Character.isWhitespace (c)) { 641 state = 0; 642 } else if (c is '='){ 643 state++; 644 } 645 break; 646 case 14: 647 state = c is '"' ? state + 1 : 0; 648 break; 649 case 15: 650 if (c is '"') state = 2; 651 break; 652 default: 653 state = 0; 654 break; 655 } 656 index+=increment; 657 } 658 if (start < length_) { 659 int tmp = parseMnemonics (buffer, start, tagStart, result); 660 int mnemonic = parseMnemonics (buffer, Math.max (tagStart, linkStart), cast(int)/*64bit*/length_, result); 661 if (mnemonic is -1) mnemonic = tmp; 662 mnemonics [linkIndex] = mnemonic; 663 } else { 664 mnemonics [linkIndex] = -1; 665 } 666 if (offsets.length !is linkIndex) { 667 Point [] newOffsets = new Point [linkIndex]; 668 System.arraycopy (offsets, 0, newOffsets, 0, linkIndex); 669 offsets = newOffsets; 670 String [] newIDs = new String [linkIndex]; 671 System.arraycopy (ids, 0, newIDs, 0, linkIndex); 672 ids = newIDs; 673 int [] newMnemonics = new int [linkIndex + 1]; 674 System.arraycopy (mnemonics, 0, newMnemonics, 0, linkIndex + 1); 675 mnemonics = newMnemonics; 676 } 677 return result.toString (); 678 } 679 680 int parseMnemonics (char[] buffer, int start, int end, StringBuffer result) { 681 int mnemonic = -1, index = start; 682 while (index < end) { 683 ptrdiff_t incr = 1; 684 if ( buffer[index] is '&') { 685 if (index + 1 < end && buffer [index + 1] is '&') { 686 result.append (buffer [index]); 687 index++; 688 } else { 689 mnemonic = result.length(); 690 } 691 } else { 692 result.append (buffer.dcharAsStringAt(index, incr)); 693 } 694 index+=incr; 695 } 696 return mnemonic; 697 } 698 699 override int setBounds(int x, int y, int width, int height, bool move, bool resize) { 700 int result = super.setBounds (x, y, width,height, move, resize); 701 if ((result & RESIZED) !is 0) { 702 layout.setWidth (width > 0 ? width : -1); 703 redraw (); 704 } 705 return result; 706 } 707 708 override void setFontDescription (PangoFontDescription* font) { 709 super.setFontDescription (font); 710 layout.setFont (Font.gtk_new (display, font)); 711 } 712 713 /** 714 * Sets the receiver's text. 715 * <p> 716 * The string can contain both regular text and hyperlinks. A hyperlink 717 * is delimited by an anchor tag, <A> and </A>. Within an 718 * anchor, a single HREF attribute is supported. When a hyperlink is 719 * selected, the text field of the selection event contains either the 720 * text of the hyperlink or the value of its HREF, if one was specified. 721 * In the rare case of identical hyperlinks within the same string, the 722 * HREF tag can be used to distinguish between them. The string may 723 * include the mnemonic character and line delimiters. 724 * </p> 725 * 726 * @param string the new text 727 * 728 * @exception SWTException <ul> 729 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 730 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 731 * </ul> 732 */ 733 public void setText (String string) { 734 checkWidget (); 735 // SWT extension: allow null for zero length string 736 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 737 if (string.equals(text)) return; 738 text = string; 739 layout.setText (parse (string)); 740 focusIndex = offsets.length > 0 ? 0 : -1; 741 selection.x = selection.y = -1; 742 bool enabled = (state & DISABLED) is 0; 743 TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null); 744 linkStyle.underline = true; 745 int [] bidiSegments = new int [offsets.length*2]; 746 for (int i = 0; i < offsets.length; i++) { 747 Point point = offsets [i]; 748 layout.setStyle (linkStyle, point.x, point.y); 749 bidiSegments[i*2] = point.x; 750 bidiSegments[i*2+1] = point.y+1; 751 } 752 layout.setSegments (bidiSegments); 753 TextStyle mnemonicStyle = new TextStyle (null, null, null); 754 mnemonicStyle.underline = true; 755 for (int i = 0; i < mnemonics.length; i++) { 756 int mnemonic = mnemonics [i]; 757 if (mnemonic !is -1) { 758 layout.setStyle (mnemonicStyle, mnemonic, mnemonic); 759 } 760 } 761 redraw (); 762 } 763 764 override void showWidget () { 765 super.showWidget (); 766 fixStyle (handle); 767 } 768 769 override int traversalCode (int key, GdkEventKey* event) { 770 if (offsets.length is 0) return 0; 771 int bits = super.traversalCode (key, event); 772 if (key is OS.GDK_Tab && focusIndex < offsets.length - 1) { 773 return bits & ~SWT.TRAVERSE_TAB_NEXT; 774 } 775 if (key is OS.GDK_ISO_Left_Tab && focusIndex > 0) { 776 return bits & ~SWT.TRAVERSE_TAB_PREVIOUS; 777 } 778 return bits; 779 } 780 }