1 11 package org.eclipse.ui.forms.widgets; 12 13 import java.io.InputStream ; 14 import java.util.ArrayList ; 15 import java.util.Enumeration ; 16 import java.util.Hashtable ; 17 18 import org.eclipse.core.runtime.ListenerList; 19 import org.eclipse.swt.SWT; 20 import org.eclipse.swt.SWTException; 21 import org.eclipse.swt.accessibility.ACC; 22 import org.eclipse.swt.accessibility.Accessible; 23 import org.eclipse.swt.accessibility.AccessibleAdapter; 24 import org.eclipse.swt.accessibility.AccessibleControlAdapter; 25 import org.eclipse.swt.accessibility.AccessibleControlEvent; 26 import org.eclipse.swt.accessibility.AccessibleEvent; 27 import org.eclipse.swt.custom.ScrolledComposite; 28 import org.eclipse.swt.dnd.Clipboard; 29 import org.eclipse.swt.dnd.TextTransfer; 30 import org.eclipse.swt.dnd.Transfer; 31 import org.eclipse.swt.events.DisposeEvent; 32 import org.eclipse.swt.events.DisposeListener; 33 import org.eclipse.swt.events.FocusEvent; 34 import org.eclipse.swt.events.FocusListener; 35 import org.eclipse.swt.events.MenuEvent; 36 import org.eclipse.swt.events.MenuListener; 37 import org.eclipse.swt.events.MouseEvent; 38 import org.eclipse.swt.events.MouseListener; 39 import org.eclipse.swt.events.MouseMoveListener; 40 import org.eclipse.swt.events.MouseTrackListener; 41 import org.eclipse.swt.events.PaintEvent; 42 import org.eclipse.swt.events.PaintListener; 43 import org.eclipse.swt.events.SelectionAdapter; 44 import org.eclipse.swt.events.SelectionEvent; 45 import org.eclipse.swt.events.SelectionListener; 46 import org.eclipse.swt.graphics.Color; 47 import org.eclipse.swt.graphics.Font; 48 import org.eclipse.swt.graphics.FontMetrics; 49 import org.eclipse.swt.graphics.GC; 50 import org.eclipse.swt.graphics.Image; 51 import org.eclipse.swt.graphics.Point; 52 import org.eclipse.swt.graphics.Rectangle; 53 import org.eclipse.swt.widgets.Canvas; 54 import org.eclipse.swt.widgets.Composite; 55 import org.eclipse.swt.widgets.Control; 56 import org.eclipse.swt.widgets.Event; 57 import org.eclipse.swt.widgets.Layout; 58 import org.eclipse.swt.widgets.Listener; 59 import org.eclipse.swt.widgets.Menu; 60 import org.eclipse.swt.widgets.MenuItem; 61 import org.eclipse.swt.widgets.TypedListener; 62 import org.eclipse.ui.forms.HyperlinkSettings; 63 import org.eclipse.ui.forms.events.HyperlinkEvent; 64 import org.eclipse.ui.forms.events.IHyperlinkListener; 65 import org.eclipse.ui.internal.forms.Messages; 66 import org.eclipse.ui.internal.forms.widgets.ControlSegment; 67 import org.eclipse.ui.internal.forms.widgets.FormTextModel; 68 import org.eclipse.ui.internal.forms.widgets.FormUtil; 69 import org.eclipse.ui.internal.forms.widgets.IFocusSelectable; 70 import org.eclipse.ui.internal.forms.widgets.IHyperlinkSegment; 71 import org.eclipse.ui.internal.forms.widgets.ImageSegment; 72 import org.eclipse.ui.internal.forms.widgets.Locator; 73 import org.eclipse.ui.internal.forms.widgets.Paragraph; 74 import org.eclipse.ui.internal.forms.widgets.ParagraphSegment; 75 import org.eclipse.ui.internal.forms.widgets.SelectionData; 76 import org.eclipse.ui.internal.forms.widgets.TextSegment; 77 78 170 public class FormText extends Canvas { 171 176 public static final String URL_HANDLER_ID = "urlHandler"; 178 181 public int marginWidth = 0; 182 183 186 public int marginHeight = 1; 187 188 private static final boolean DEBUG_TEXT = false; private static final boolean DEBUG_TEXTSIZE = false; 193 private static final boolean DEBUG_FOCUS = false; 195 private boolean hasFocus; 196 197 private boolean paragraphsSeparated = true; 198 199 private FormTextModel model; 200 201 private ListenerList listeners; 202 203 private Hashtable resourceTable = new Hashtable (); 204 205 private IHyperlinkSegment entered; 206 207 private IHyperlinkSegment armed; 208 209 private boolean mouseFocus = false; 210 211 private boolean controlFocusTransfer = false; 212 213 private boolean inSelection = false; 214 215 private SelectionData selData; 216 217 private static final String INTERNAL_MENU = "__internal_menu__"; 219 private static final String CONTROL_KEY = "__segment__"; 221 private class FormTextLayout extends Layout implements ILayoutExtension { 222 public FormTextLayout() { 223 } 224 225 public int computeMaximumWidth(Composite parent, boolean changed) { 226 return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x; 227 } 228 229 public int computeMinimumWidth(Composite parent, boolean changed) { 230 return computeSize(parent, 5, SWT.DEFAULT, true).x; 231 } 232 233 236 public Point computeSize(Composite composite, int wHint, int hHint, 237 boolean changed) { 238 long start = 0; 239 240 if (DEBUG_TEXT) 241 start = System.currentTimeMillis(); 242 int innerWidth = wHint; 243 if (innerWidth != SWT.DEFAULT) 244 innerWidth -= marginWidth * 2; 245 Point textSize = computeTextSize(innerWidth); 246 int textWidth = textSize.x + 2 * marginWidth; 247 int textHeight = textSize.y + 2 * marginHeight; 248 Point result = new Point(textWidth, textHeight); 249 if (DEBUG_TEXT) { 250 long stop = System.currentTimeMillis(); 251 System.out.println("FormText computeSize: " + (stop - start) + "ms"); } 254 if (DEBUG_TEXTSIZE) { 255 System.out.println("FormText ("+model.getAccessibleText()+"), computeSize: wHint="+wHint+", result="+result); } 257 return result; 258 } 259 260 private Point computeTextSize(int wHint) { 261 Paragraph[] paragraphs = model.getParagraphs(); 262 GC gc = new GC(FormText.this); 263 gc.setFont(getFont()); 264 Locator loc = new Locator(); 265 int width = wHint != SWT.DEFAULT ? wHint : 0; 266 FontMetrics fm = gc.getFontMetrics(); 267 int lineHeight = fm.getHeight(); 268 boolean selectableInTheLastRow = false; 269 for (int i = 0; i < paragraphs.length; i++) { 270 Paragraph p = paragraphs[i]; 271 if (i > 0 && getParagraphsSeparated() 272 && p.getAddVerticalSpace()) 273 loc.y += getParagraphSpacing(lineHeight); 274 loc.rowHeight = 0; 275 loc.indent = p.getIndent(); 276 loc.x = p.getIndent(); 277 ParagraphSegment[] segments = p.getSegments(); 278 if (segments.length > 0) { 279 selectableInTheLastRow = false; 280 int pwidth = 0; 281 for (int j = 0; j < segments.length; j++) { 282 ParagraphSegment segment = segments[j]; 283 segment.advanceLocator(gc, wHint, loc, resourceTable, 284 false); 285 if (wHint != SWT.DEFAULT) { 286 width = Math.max(width, loc.width); 287 } else { 288 pwidth += loc.width; 289 } 290 if (segment instanceof IFocusSelectable) 291 selectableInTheLastRow = true; 292 } 293 if (wHint == SWT.DEFAULT) 294 width = Math.max(width, pwidth); 295 loc.y += loc.rowHeight; 296 } else { 297 loc.y += lineHeight; 299 } 300 } 301 gc.dispose(); 302 if (selectableInTheLastRow) 303 loc.y += 1; 304 return new Point(width, loc.y); 305 } 306 307 protected void layout(Composite composite, boolean flushCache) { 308 long start = 0; 309 310 if (DEBUG_TEXT) { 311 start = System.currentTimeMillis(); 312 } 313 selData = null; 314 Rectangle carea = composite.getClientArea(); 315 if (DEBUG_TEXTSIZE) { 316 System.out.println("FormText layout ("+model.getAccessibleText()+"), carea="+carea); } 318 GC gc = new GC(composite); 319 gc.setFont(getFont()); 320 ensureBoldFontPresent(getFont()); 321 gc.setForeground(getForeground()); 322 gc.setBackground(getBackground()); 323 324 Locator loc = new Locator(); 325 loc.marginWidth = marginWidth; 326 loc.marginHeight = marginHeight; 327 loc.x = marginWidth; 328 loc.y = marginHeight; 329 FontMetrics fm = gc.getFontMetrics(); 330 int lineHeight = fm.getHeight(); 331 332 Paragraph[] paragraphs = model.getParagraphs(); 333 IHyperlinkSegment selectedLink = getSelectedLink(); 334 for (int i = 0; i < paragraphs.length; i++) { 335 Paragraph p = paragraphs[i]; 336 if (i > 0 && paragraphsSeparated && p.getAddVerticalSpace()) 337 loc.y += getParagraphSpacing(lineHeight); 338 loc.indent = p.getIndent(); 339 loc.resetCaret(); 340 loc.rowHeight = 0; 341 p.layout(gc, carea.width, loc, lineHeight, resourceTable, 342 selectedLink); 343 } 344 gc.dispose(); 345 if (DEBUG_TEXT) { 346 long stop = System.currentTimeMillis(); 347 System.out.println("FormText.layout: " + (stop - start) + "ms"); } 349 } 350 } 351 352 361 public FormText(Composite parent, int style) { 362 super(parent, SWT.NO_BACKGROUND | SWT.WRAP | style); 363 setLayout(new FormTextLayout()); 364 model = new FormTextModel(); 365 addDisposeListener(new DisposeListener() { 366 public void widgetDisposed(DisposeEvent e) { 367 model.dispose(); 368 disposeResourceTable(true); 369 } 370 }); 371 addPaintListener(new PaintListener() { 372 public void paintControl(PaintEvent e) { 373 paint(e); 374 } 375 }); 376 addListener(SWT.KeyDown, new Listener() { 377 public void handleEvent(Event e) { 378 if (e.character == '\r') { 379 activateSelectedLink(); 380 return; 381 } 382 } 383 }); 384 addListener(SWT.Traverse, new Listener() { 385 public void handleEvent(Event e) { 386 if (DEBUG_FOCUS) 387 System.out.println("Traversal: " + e); switch (e.detail) { 389 case SWT.TRAVERSE_PAGE_NEXT: 390 case SWT.TRAVERSE_PAGE_PREVIOUS: 391 case SWT.TRAVERSE_ARROW_NEXT: 392 case SWT.TRAVERSE_ARROW_PREVIOUS: 393 e.doit = false; 394 return; 395 } 396 if (!model.hasFocusSegments()) { 397 e.doit = true; 398 return; 399 } 400 if (e.detail == SWT.TRAVERSE_TAB_NEXT) 401 e.doit = advance(true); 402 else if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) 403 e.doit = advance(false); 404 else if (e.detail != SWT.TRAVERSE_RETURN) 405 e.doit = true; 406 } 407 }); 408 addFocusListener(new FocusListener() { 409 public void focusGained(FocusEvent e) { 410 if (!hasFocus) { 411 hasFocus = true; 412 if (DEBUG_FOCUS) { 413 System.out.println("FormText: focus gained"); } 415 if (!mouseFocus && !controlFocusTransfer) { 416 handleFocusChange(); 417 } 418 } 419 } 420 421 public void focusLost(FocusEvent e) { 422 if (DEBUG_FOCUS) { 423 System.out.println("FormText: focus lost"); } 425 if (hasFocus) { 426 hasFocus = false; 427 if (!controlFocusTransfer) 428 handleFocusChange(); 429 } 430 } 431 }); 432 addMouseListener(new MouseListener() { 433 public void mouseDoubleClick(MouseEvent e) { 434 } 435 436 public void mouseDown(MouseEvent e) { 437 handleMouseClick(e, true); 439 } 440 441 public void mouseUp(MouseEvent e) { 442 handleMouseClick(e, false); 444 } 445 }); 446 addMouseTrackListener(new MouseTrackListener() { 447 public void mouseEnter(MouseEvent e) { 448 handleMouseMove(e); 449 } 450 451 public void mouseExit(MouseEvent e) { 452 if (entered != null) { 453 exitLink(entered, e.stateMask); 454 paintLinkHover(entered, false); 455 entered = null; 456 setCursor(null); 457 } 458 } 459 460 public void mouseHover(MouseEvent e) { 461 handleMouseHover(e); 462 } 463 }); 464 addMouseMoveListener(new MouseMoveListener() { 465 public void mouseMove(MouseEvent e) { 466 handleMouseMove(e); 467 } 468 }); 469 initAccessible(); 470 ensureBoldFontPresent(getFont()); 471 createMenu(); 472 setTabList(new Control[] {}); 474 } 475 476 481 public boolean getFocus() { 482 return hasFocus; 483 } 484 485 493 public boolean isLoading() { 494 return false; 495 } 496 497 504 public String getLoadingText() { 505 return null; 506 } 507 508 518 public void setLoadingText(String loadingText) { 519 setText(loadingText, false, false); 520 } 521 522 531 public void setParagraphsSeparated(boolean value) { 532 paragraphsSeparated = value; 533 } 534 535 541 public boolean getParagraphsSeparated() { 542 return paragraphsSeparated; 543 } 544 545 558 public void setImage(String key, Image image) { 559 resourceTable.put("i." + key, image); } 561 562 576 public void setColor(String key, Color color) { 577 String fullKey = "c." + key; if (color == null) 579 resourceTable.remove(fullKey); 580 else 581 resourceTable.put(fullKey, color); 582 } 583 584 598 public void setFont(String key, Font font) { 599 String fullKey = "f." + key; if (font == null) 601 resourceTable.remove(fullKey); 602 else 603 resourceTable.put(fullKey, font); 604 model.clearCache(fullKey); 605 } 606 607 623 public void setControl(String key, Control control) { 624 String fullKey = "o." + key; if (control == null) 626 resourceTable.remove(fullKey); 627 else 628 resourceTable.put(fullKey, control); 629 } 630 631 639 public void setFont(Font font) { 640 super.setFont(font); 641 model.clearCache(null); 642 Font boldFont = (Font) resourceTable.get(FormTextModel.BOLD_FONT_ID); 643 if (boldFont != null) { 644 boldFont.dispose(); 645 resourceTable.remove(FormTextModel.BOLD_FONT_ID); 646 } 647 ensureBoldFontPresent(getFont()); 648 } 649 650 664 public void setText(String text, boolean parseTags, boolean expandURLs) { 665 disposeResourceTable(false); 666 entered = null; 667 if (parseTags) 668 model.parseTaggedText(text, expandURLs); 669 else 670 model.parseRegularText(text, expandURLs); 671 hookControlSegmentFocus(); 672 layout(); 673 redraw(); 674 } 675 676 687 public void setContents(InputStream is, boolean expandURLs) { 688 entered = null; 689 disposeResourceTable(false); 690 model.parseInputStream(is, expandURLs); 691 hookControlSegmentFocus(); 692 layout(); 693 redraw(); 694 } 695 696 private void hookControlSegmentFocus() { 697 Paragraph[] paragraphs = model.getParagraphs(); 698 if (paragraphs == null) 699 return; 700 Listener listener = new Listener() { 701 public void handleEvent(Event e) { 702 switch (e.type) { 703 case SWT.FocusIn: 704 if (!controlFocusTransfer) 705 syncControlSegmentFocus((Control) e.widget); 706 break; 707 case SWT.Traverse: 708 if (DEBUG_FOCUS) 709 System.out.println("Control traversal: " + e); switch (e.detail) { 711 case SWT.TRAVERSE_PAGE_NEXT: 712 case SWT.TRAVERSE_PAGE_PREVIOUS: 713 case SWT.TRAVERSE_ARROW_NEXT: 714 case SWT.TRAVERSE_ARROW_PREVIOUS: 715 e.doit = false; 716 return; 717 } 718 Control c = (Control) e.widget; 719 ControlSegment segment = (ControlSegment) c 720 .getData(CONTROL_KEY); 721 if (e.detail == SWT.TRAVERSE_TAB_NEXT) 722 e.doit = advanceControl(c, segment, true); 723 else if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) 724 e.doit = advanceControl(c, segment, false); 725 if (!e.doit) 726 e.detail = SWT.TRAVERSE_NONE; 727 break; 728 } 729 } 730 }; 731 for (int i = 0; i < paragraphs.length; i++) { 732 Paragraph p = paragraphs[i]; 733 ParagraphSegment[] segments = p.getSegments(); 734 for (int j = 0; j < segments.length; j++) { 735 if (segments[j] instanceof ControlSegment) { 736 ControlSegment cs = (ControlSegment) segments[j]; 737 Control c = cs.getControl(resourceTable); 738 if (c != null) { 739 if (c.getData(CONTROL_KEY) == null) { 740 c.setData(CONTROL_KEY, cs); 742 attachTraverseListener(c, listener); 743 } 744 } 745 } 746 } 747 } 748 } 749 750 private void attachTraverseListener(Control c, Listener listener) { 751 if (c instanceof Composite) { 752 Composite parent = (Composite) c; 753 Control[] children = parent.getChildren(); 754 for (int i = 0; i < children.length; i++) { 755 attachTraverseListener(children[i], listener); 756 } 757 if (c instanceof Canvas) { 758 c.addListener(SWT.Traverse, listener); 761 c.addListener(SWT.FocusIn, listener); 762 } 763 } else { 764 c.addListener(SWT.Traverse, listener); 765 c.addListener(SWT.FocusIn, listener); 766 } 767 } 768 769 778 private void syncControlSegmentFocus(Control control) { 779 ControlSegment cs = null; 780 781 while (control != null) { 782 cs = (ControlSegment) control.getData(CONTROL_KEY); 783 if (cs != null) 784 break; 785 control = control.getParent(); 786 } 787 if (cs == null) 788 return; 789 IFocusSelectable current = model.getSelectedSegment(); 790 if (current == cs) 792 return; 793 IHyperlinkSegment oldLink = null; 794 if (current != null && current instanceof IHyperlinkSegment) { 795 oldLink = (IHyperlinkSegment) current; 796 exitLink(oldLink, SWT.NULL); 797 } 798 if (DEBUG_FOCUS) 799 System.out.println("Sync control: " + cs + ", oldLink=" + oldLink); model.select(cs); 801 if (oldLink != null) 802 paintFocusTransfer(oldLink, null); 803 } 805 806 private boolean advanceControl(Control c, ControlSegment segment, 807 boolean next) { 808 Composite parent = c.getParent(); 809 if (parent == this) { 810 IFocusSelectable nextSegment = model.getNextFocusSegment(next); 812 if (nextSegment != null) { 813 controlFocusTransfer = true; 814 super.forceFocus(); 815 controlFocusTransfer = false; 816 model.select(segment); 817 return advance(next); 818 } 819 return setFocusToNextSibling(this, next); 821 } 822 if (setFocusToNextSibling(c, next)) 823 return true; 824 segment = (ControlSegment) parent.getData(CONTROL_KEY); 826 return advanceControl(parent, segment, next); 827 } 828 829 private boolean setFocusToNextSibling(Control c, boolean next) { 830 Composite parent = c.getParent(); 831 Control[] children = parent.getTabList(); 832 for (int i = 0; i < children.length; i++) { 833 Control child = children[i]; 834 if (child == c) { 835 if (next) { 837 for (int j = i + 1; j < children.length; j++) { 838 Control nc = children[j]; 839 if (nc.setFocus()) 840 return false; 841 } 842 } else { 843 for (int j = i - 1; j >= 0; j--) { 844 Control pc = children[j]; 845 if (pc.setFocus()) 846 return false; 847 } 848 } 849 } 850 } 851 return false; 852 } 853 854 873 public void setWhitespaceNormalized(boolean value) { 874 model.setWhitespaceNormalized(value); 875 } 876 877 884 public boolean isWhitespaceNormalized() { 885 return model.isWhitespaceNormalized(); 886 } 887 888 895 public void setMenu(Menu menu) { 896 Menu currentMenu = super.getMenu(); 897 if (currentMenu != null && INTERNAL_MENU.equals(currentMenu.getData())) { 898 if (menu != null) { 900 currentMenu.dispose(); 901 super.setMenu(menu); 902 } 903 } else 904 super.setMenu(menu); 905 } 906 907 private void createMenu() { 908 Menu menu = new Menu(this); 909 final MenuItem copyItem = new MenuItem(menu, SWT.PUSH); 910 copyItem.setText(Messages.FormText_copy); 911 912 SelectionListener listener = new SelectionAdapter() { 913 public void widgetSelected(SelectionEvent e) { 914 if (e.widget == copyItem) { 915 copy(); 916 } 917 } 918 }; 919 copyItem.addSelectionListener(listener); 920 menu.addMenuListener(new MenuListener() { 921 public void menuShown(MenuEvent e) { 922 copyItem.setEnabled(canCopy()); 923 } 924 925 public void menuHidden(MenuEvent e) { 926 } 927 }); 928 menu.setData(INTERNAL_MENU); 929 super.setMenu(menu); 930 } 931 932 937 public HyperlinkSettings getHyperlinkSettings() { 938 return model.getHyperlinkSettings(); 939 } 940 941 948 public void setHyperlinkSettings(HyperlinkSettings settings) { 949 model.setHyperlinkSettings(settings); 950 } 951 952 958 public void addHyperlinkListener(IHyperlinkListener listener) { 959 if (listeners == null) 960 listeners = new ListenerList(); 961 listeners.add(listener); 962 } 963 964 970 public void removeHyperlinkListener(IHyperlinkListener listener) { 971 if (listeners == null) 972 return; 973 listeners.remove(listener); 974 } 975 976 998 public void addSelectionListener(SelectionListener listener) { 999 checkWidget(); 1000 if (listener == null) { 1001 SWT.error(SWT.ERROR_NULL_ARGUMENT); 1002 } 1003 TypedListener typedListener = new TypedListener(listener); 1004 addListener(SWT.Selection, typedListener); 1005 } 1006 1007 1026 public void removeSelectionListener(SelectionListener listener) { 1027 checkWidget(); 1028 if (listener == null) { 1029 SWT.error(SWT.ERROR_NULL_ARGUMENT); 1030 } 1031 removeListener(SWT.Selection, listener); 1032 } 1033 1034 1048 1049 public String getSelectionText() { 1050 checkWidget(); 1051 if (selData != null) 1052 return selData.getSelectionText(); 1053 return ""; } 1055 1056 1063 public boolean canCopy() { 1064 return selData != null && selData.canCopy(); 1065 } 1066 1067 1073 1074 public void copy() { 1075 if (!canCopy()) 1076 return; 1077 Clipboard clipboard = new Clipboard(getDisplay()); 1078 Object [] o = new Object [] { getSelectionText() }; 1079 Transfer[] t = new Transfer[] { TextTransfer.getInstance() }; 1080 clipboard.setContents(o, t); 1081 clipboard.dispose(); 1082 } 1083 1084 1093 public Object getSelectedLinkHref() { 1094 IHyperlinkSegment link = getSelectedLink(); 1095 return link != null ? link.getHref() : null; 1096 } 1097 1098 1107 public String getSelectedLinkText() { 1108 IHyperlinkSegment link = getSelectedLink(); 1109 return link != null ? link.getText() : null; 1110 } 1111 1112 private IHyperlinkSegment getSelectedLink() { 1113 IFocusSelectable segment = model.getSelectedSegment(); 1114 if (segment != null && segment instanceof IHyperlinkSegment) 1115 return (IHyperlinkSegment) segment; 1116 return null; 1117 } 1118 1119 private void initAccessible() { 1120 Accessible accessible = getAccessible(); 1121 accessible.addAccessibleListener(new AccessibleAdapter() { 1122 public void getName(AccessibleEvent e) { 1123 if (e.childID == ACC.CHILDID_SELF) 1124 e.result = model.getAccessibleText(); 1125 else { 1126 int linkCount = model.getHyperlinkCount(); 1127 if (e.childID >= 0 && e.childID < linkCount) { 1128 IHyperlinkSegment link = model.getHyperlink(e.childID); 1129 e.result = link.getText(); 1130 } 1131 } 1132 } 1133 1134 public void getHelp(AccessibleEvent e) { 1135 e.result = getToolTipText(); 1136 int linkCount = model.getHyperlinkCount(); 1137 if (e.result == null && e.childID >= 0 && e.childID < linkCount) { 1138 IHyperlinkSegment link = model.getHyperlink(e.childID); 1139 e.result = link.getText(); 1140 } 1141 } 1142 }); 1143 accessible.addAccessibleControlListener(new AccessibleControlAdapter() { 1144 public void getChildAtPoint(AccessibleControlEvent e) { 1145 Point pt = toControl(new Point(e.x, e.y)); 1146 IHyperlinkSegment link = model.findHyperlinkAt(pt.x, pt.y); 1147 if (link != null) 1148 e.childID = model.indexOf(link); 1149 else 1150 e.childID = ACC.CHILDID_SELF; 1151 } 1152 1153 public void getLocation(AccessibleControlEvent e) { 1154 Rectangle location = null; 1155 if (e.childID != ACC.CHILDID_SELF 1156 && e.childID != ACC.CHILDID_NONE) { 1157 int index = e.childID; 1158 IHyperlinkSegment link = model.getHyperlink(index); 1159 if (link != null) { 1160 location = link.getBounds(); 1161 } 1162 } 1163 if (location == null) { 1164 location = getBounds(); 1165 } 1166 Point pt = toDisplay(new Point(location.x, location.y)); 1167 e.x = pt.x; 1168 e.y = pt.y; 1169 e.width = location.width; 1170 e.height = location.height; 1171 } 1172 1173 public void getFocus(AccessibleControlEvent e) { 1174 int childID = ACC.CHILDID_NONE; 1175 1176 if (isFocusControl()) { 1177 int selectedIndex = model.getSelectedSegmentIndex(); 1178 if (selectedIndex == -1) { 1179 childID = ACC.CHILDID_SELF; 1180 } else { 1181 childID = selectedIndex; 1182 } 1183 } 1184 e.childID = childID; 1185 } 1186 1187 public void getDefaultAction (AccessibleControlEvent e) { 1188 if (model.getHyperlinkCount() > 0) { 1189 e.result = SWT.getMessage ("SWT_Press"); } 1191 } 1192 1193 public void getChildCount(AccessibleControlEvent e) { 1194 e.detail = model.getHyperlinkCount(); 1195 } 1196 1197 public void getRole(AccessibleControlEvent e) { 1198 int role = 0; 1199 int childID = e.childID; 1200 int linkCount = model.getHyperlinkCount(); 1201 if (childID == ACC.CHILDID_SELF) { 1202 if (linkCount > 0) { 1203 role = ACC.ROLE_LINK; 1204 } else { 1205 role = ACC.ROLE_TEXT; 1206 } 1207 } else if (childID >= 0 && childID < linkCount) { 1208 role = ACC.ROLE_LINK; 1209 } 1210 e.detail = role; 1211 } 1212 1213 public void getSelection(AccessibleControlEvent e) { 1214 int selectedIndex = model.getSelectedSegmentIndex(); 1215 e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE 1216 : selectedIndex; 1217 } 1218 1219 public void getState(AccessibleControlEvent e) { 1220 int linkCount = model.getHyperlinkCount(); 1221 int selectedIndex = model.getSelectedSegmentIndex(); 1222 int state = 0; 1223 int childID = e.childID; 1224 if (childID == ACC.CHILDID_SELF) { 1225 state = ACC.STATE_NORMAL; 1226 } else if (childID >= 0 && childID < linkCount) { 1227 state = ACC.STATE_SELECTABLE; 1228 if (isFocusControl()) { 1229 state |= ACC.STATE_FOCUSABLE; 1230 } 1231 if (selectedIndex == childID) { 1232 state |= ACC.STATE_SELECTED; 1233 if (isFocusControl()) { 1234 state |= ACC.STATE_FOCUSED; 1235 } 1236 } 1237 } 1238 state |= ACC.STATE_READONLY; 1239 e.detail = state; 1240 } 1241 1242 public void getChildren(AccessibleControlEvent e) { 1243 int linkCount = model.getHyperlinkCount(); 1244 Object [] children = new Object [linkCount]; 1245 for (int i = 0; i < linkCount; i++) { 1246 children[i] = new Integer (i); 1247 } 1248 e.children = children; 1249 } 1250 1251 public void getValue(AccessibleControlEvent e) { 1252 } 1254 }); 1255 } 1256 1257 private void startSelection(MouseEvent e) { 1258 inSelection = true; 1259 selData = new SelectionData(e); 1260 redraw(); 1261 Form form = FormUtil.getForm(this); 1262 if (form != null) 1263 form.setSelectionText(this); 1264 } 1265 1266 private void endSelection(MouseEvent e) { 1267 inSelection = false; 1268 if (selData != null) { 1269 if (!selData.isEnclosed()) 1270 selData = null; 1271 else 1272 computeSelection(); 1273 } 1274 notifySelectionChanged(); 1275 } 1276 1277 private void computeSelection() { 1278 GC gc = new GC(this); 1279 Paragraph[] paragraphs = model.getParagraphs(); 1280 IHyperlinkSegment selectedLink = getSelectedLink(); 1281 if (getDisplay().getFocusControl() != this) 1282 selectedLink = null; 1283 for (int i = 0; i < paragraphs.length; i++) { 1284 Paragraph p = paragraphs[i]; 1285 if (i > 0) 1286 selData.markNewLine(); 1287 p.computeSelection(gc, resourceTable, selectedLink, selData); 1288 } 1289 gc.dispose(); 1290 } 1291 1292 void clearSelection() { 1293 selData = null; 1294 if (!isDisposed()) { 1295 redraw(); 1296 notifySelectionChanged(); 1297 } 1298 } 1299 1300 private void notifySelectionChanged() { 1301 Event event = new Event(); 1302 event.widget = this; 1303 event.display = this.getDisplay(); 1304 event.type = SWT.Selection; 1305 notifyListeners(SWT.Selection, event); 1306 getAccessible().selectionChanged(); 1307 } 1308 1309 private void handleDrag(MouseEvent e) { 1310 if (selData != null) { 1311 ScrolledComposite scomp = FormUtil.getScrolledComposite(this); 1312 if (scomp != null) { 1313 FormUtil.ensureVisible(scomp, this, e); 1314 } 1315 selData.update(e); 1316 redraw(); 1317 } 1318 } 1319 1320 private void handleMouseClick(MouseEvent e, boolean down) { 1321 if (DEBUG_FOCUS) 1322 System.out.println("FormText: mouse click(" + down + ")"); if (down) { 1324 mouseFocus = true; 1326 IHyperlinkSegment segmentUnder = model.findHyperlinkAt(e.x, e.y); 1327 if (segmentUnder != null) { 1328 IHyperlinkSegment oldLink = getSelectedLink(); 1329 if (getDisplay().getFocusControl() != this) { 1330 setFocus(); 1331 } 1332 model.selectLink(segmentUnder); 1333 enterLink(segmentUnder, e.stateMask); 1334 paintFocusTransfer(oldLink, segmentUnder); 1335 } 1336 if (e.button == 1) { 1337 startSelection(e); 1338 armed = segmentUnder; 1339 } 1340 else { 1341 } 1342 } else { 1343 if (e.button == 1) { 1344 endSelection(e); 1345 IHyperlinkSegment segmentUnder = model 1346 .findHyperlinkAt(e.x, e.y); 1347 if (segmentUnder != null && armed == segmentUnder && selData == null) { 1348 activateLink(segmentUnder, e.stateMask); 1349 armed = null; 1350 } 1351 } 1352 mouseFocus = false; 1353 } 1354 } 1355 1356 private void handleMouseHover(MouseEvent e) { 1357 } 1358 1359 private void updateTooltipText(ParagraphSegment segment) { 1360 String tooltipText = null; 1361 if (segment != null) { 1362 tooltipText = segment.getTooltipText(); 1363 } 1364 String currentTooltipText = getToolTipText(); 1365 1366 if ((currentTooltipText != null && tooltipText == null) 1367 || (currentTooltipText == null && tooltipText != null)) 1368 setToolTipText(tooltipText); 1369 } 1370 1371 private void handleMouseMove(MouseEvent e) { 1372 if (inSelection) { 1373 handleDrag(e); 1374 return; 1375 } 1376 ParagraphSegment segmentUnder = model.findSegmentAt(e.x, e.y); 1377 updateTooltipText(segmentUnder); 1378 if (segmentUnder == null) { 1379 if (entered != null) { 1380 exitLink(entered, e.stateMask); 1381 paintLinkHover(entered, false); 1382 entered = null; 1383 } 1384 setCursor(null); 1385 } else { 1386 if (segmentUnder instanceof IHyperlinkSegment) { 1387 IHyperlinkSegment linkUnder = (IHyperlinkSegment) segmentUnder; 1388 if (entered!=null && linkUnder!=entered) { 1389 exitLink(entered, e.stateMask); 1392 paintLinkHover(entered, false); 1393 entered = null; 1394 } 1395 if (entered == null) { 1396 entered = linkUnder; 1397 enterLink(linkUnder, e.stateMask); 1398 paintLinkHover(entered, true); 1399 setCursor(model.getHyperlinkSettings().getHyperlinkCursor()); 1400 } 1401 } else { 1402 if (entered != null) { 1403 exitLink(entered, e.stateMask); 1404 paintLinkHover(entered, false); 1405 entered = null; 1406 } 1407 if (segmentUnder instanceof TextSegment) 1408 setCursor(model.getHyperlinkSettings().getTextCursor()); 1409 else 1410 setCursor(null); 1411 } 1412 } 1413 } 1414 1415 private boolean advance(boolean next) { 1416 if (DEBUG_FOCUS) 1417 System.out.println("Advance: next=" + next); IFocusSelectable current = model.getSelectedSegment(); 1419 if (current != null && current instanceof IHyperlinkSegment) 1420 exitLink((IHyperlinkSegment) current, SWT.NULL); 1421 IFocusSelectable newSegment = null; 1422 boolean valid = false; 1423 while (!valid) { 1426 if (!model.traverseFocusSelectableObjects(next)) 1427 break; 1428 newSegment = model.getSelectedSegment(); 1429 if (newSegment == null) 1430 break; 1431 valid = setControlFocus(next, newSegment); 1432 } 1433 IHyperlinkSegment newLink = newSegment instanceof IHyperlinkSegment ? (IHyperlinkSegment) newSegment 1434 : null; 1435 if (valid) 1436 enterLink(newLink, SWT.NULL); 1437 IHyperlinkSegment oldLink = current instanceof IHyperlinkSegment ? (IHyperlinkSegment) current 1438 : null; 1439 if (oldLink != null || newLink != null) 1440 paintFocusTransfer(oldLink, newLink); 1441 if (newLink != null) 1442 ensureVisible(newLink); 1443 if (newLink != null) 1444 getAccessible().setFocus(model.getSelectedSegmentIndex()); 1445 return !valid; 1446 } 1447 1448 private boolean setControlFocus(boolean next, IFocusSelectable selectable) { 1449 controlFocusTransfer = true; 1450 boolean result = selectable.setFocus(resourceTable, next); 1451 controlFocusTransfer = false; 1452 return result; 1453 } 1454 1455 private void handleFocusChange() { 1456 if (DEBUG_FOCUS) { 1457 System.out.println("Handle focus change: hasFocus=" + hasFocus + ", mouseFocus=" + mouseFocus); } 1460 if (hasFocus) { 1461 boolean advance = true; 1462 if (!mouseFocus) { 1463 boolean valid = false; 1465 IFocusSelectable selectable = null; 1466 while (!valid) { 1467 if (!model.traverseFocusSelectableObjects(advance)) 1468 break; 1469 selectable = model.getSelectedSegment(); 1470 if (selectable == null) 1471 break; 1472 valid = setControlFocus(advance, selectable); 1473 } 1474 if (selectable != null) 1475 ensureVisible(selectable); 1476 if (selectable instanceof IHyperlinkSegment) { 1477 enterLink((IHyperlinkSegment) selectable, SWT.NULL); 1478 paintFocusTransfer(null, (IHyperlinkSegment) selectable); 1479 } 1480 } 1481 } else { 1482 paintFocusTransfer(getSelectedLink(), null); 1483 model.selectLink(null); 1484 } 1485 } 1486 1487 private void enterLink(IHyperlinkSegment link, int stateMask) { 1488 if (link == null || listeners == null) 1489 return; 1490 int size = listeners.size(); 1491 HyperlinkEvent he = new HyperlinkEvent(this, link.getHref(), link 1492 .getText(), stateMask); 1493 Object [] listenerList = listeners.getListeners(); 1494 for (int i = 0; i < size; i++) { 1495 IHyperlinkListener listener = (IHyperlinkListener) listenerList[i]; 1496 listener.linkEntered(he); 1497 } 1498 } 1499 1500 private void exitLink(IHyperlinkSegment link, int stateMask) { 1501 if (link == null || listeners == null) 1502 return; 1503 int size = listeners.size(); 1504 HyperlinkEvent he = new HyperlinkEvent(this, link.getHref(), link 1505 .getText(), stateMask); 1506 Object [] listenerList = listeners.getListeners(); 1507 for (int i = 0; i < size; i++) { 1508 IHyperlinkListener listener = (IHyperlinkListener) listenerList[i]; 1509 listener.linkExited(he); 1510 } 1511 } 1512 1513 private void paintLinkHover(IHyperlinkSegment link, boolean hover) { 1514 GC gc = new GC(this); 1515 HyperlinkSettings settings = getHyperlinkSettings(); 1516 Color newFg = hover ? settings.getActiveForeground() : settings 1517 .getForeground(); 1518 if (newFg != null) 1519 gc.setForeground(newFg); 1520 gc.setBackground(getBackground()); 1521 gc.setFont(getFont()); 1522 boolean selected = (link == getSelectedLink()); 1523 ((ParagraphSegment) link).paint(gc, hover, resourceTable, selected, 1524 selData, null); 1525 gc.dispose(); 1526 } 1527 1528 private void activateSelectedLink() { 1529 IHyperlinkSegment link = getSelectedLink(); 1530 if (link != null) 1531 activateLink(link, SWT.NULL); 1532 } 1533 1534 private void activateLink(IHyperlinkSegment link, int stateMask) { 1535 setCursor(model.getHyperlinkSettings().getBusyCursor()); 1536 if (listeners != null) { 1537 int size = listeners.size(); 1538 HyperlinkEvent e = new HyperlinkEvent(this, link.getHref(), link 1539 .getText(), stateMask); 1540 Object [] listenerList = listeners.getListeners(); 1541 for (int i = 0; i < size; i++) { 1542 IHyperlinkListener listener = (IHyperlinkListener) listenerList[i]; 1543 listener.linkActivated(e); 1544 } 1545 } 1546 if (!isDisposed() && model.linkExists(link)) { 1547 setCursor(model.getHyperlinkSettings().getHyperlinkCursor()); 1548 } 1549 } 1550 1551 private void ensureBoldFontPresent(Font regularFont) { 1552 Font boldFont = (Font) resourceTable.get(FormTextModel.BOLD_FONT_ID); 1553 if (boldFont != null) 1554 return; 1555 boldFont = FormUtil.createBoldFont(getDisplay(), regularFont); 1556 resourceTable.put(FormTextModel.BOLD_FONT_ID, boldFont); 1557 } 1558 1559 private void paint(PaintEvent e) { 1560 GC gc = e.gc; 1561 gc.setFont(getFont()); 1562 ensureBoldFontPresent(getFont()); 1563 gc.setForeground(getForeground()); 1564 gc.setBackground(getBackground()); 1565 repaint(gc, e.x, e.y, e.width, e.height); 1566 } 1567 1568 private void repaint(GC gc, int x, int y, int width, int height) { 1569 Image textBuffer = new Image(getDisplay(), width, height); 1570 Color bg = getBackground(); 1571 Color fg = getForeground(); 1572 if (!getEnabled()) { 1573 bg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); 1574 fg = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); 1575 } 1576 GC textGC = new GC(textBuffer, gc.getStyle()); 1577 textGC.setForeground(fg); 1578 textGC.setBackground(bg); 1579 textGC.setFont(getFont()); 1580 textGC.fillRectangle(0, 0, width, height); 1581 Rectangle repaintRegion = new Rectangle(x, y, width, height); 1582 1583 Paragraph[] paragraphs = model.getParagraphs(); 1584 IHyperlinkSegment selectedLink = getSelectedLink(); 1585 if (getDisplay().getFocusControl() != this) 1586 selectedLink = null; 1587 for (int i = 0; i < paragraphs.length; i++) { 1588 Paragraph p = paragraphs[i]; 1589 p 1590 .paint(textGC, repaintRegion, resourceTable, selectedLink, 1591 selData); 1592 } 1593 textGC.dispose(); 1594 gc.drawImage(textBuffer, x, y); 1595 textBuffer.dispose(); 1596 } 1597 1598 private int getParagraphSpacing(int lineHeight) { 1599 return lineHeight / 2; 1600 } 1601 1602 private void paintFocusTransfer(IHyperlinkSegment oldLink, 1603 IHyperlinkSegment newLink) { 1604 GC gc = new GC(this); 1605 Color bg = getBackground(); 1606 Color fg = getForeground(); 1607 gc.setFont(getFont()); 1608 if (oldLink != null) { 1609 gc.setBackground(bg); 1610 gc.setForeground(fg); 1611 oldLink.paintFocus(gc, bg, fg, false, null); 1612 } 1613 if (newLink != null) { 1614 gc.setBackground(bg); 1616 gc.setForeground(fg); 1617 newLink.paintFocus(gc, bg, fg, true, null); 1618 } 1619 gc.dispose(); 1620 } 1621 1622 private void ensureVisible(IFocusSelectable segment) { 1623 if (mouseFocus) { 1624 mouseFocus = false; 1625 return; 1626 } 1627 if (segment == null) 1628 return; 1629 Rectangle bounds = segment.getBounds(); 1630 ScrolledComposite scomp = FormUtil.getScrolledComposite(this); 1631 if (scomp == null) 1632 return; 1633 Point origin = FormUtil.getControlLocation(scomp, this); 1634 origin.x += bounds.x; 1635 origin.y += bounds.y; 1636 FormUtil.ensureVisible(scomp, origin, new Point(bounds.width, 1637 bounds.height)); 1638 } 1639 1640 1648 public Point computeSize(int wHint, int hHint, boolean changed) { 1649 checkWidget(); 1650 Point size; 1651 FormTextLayout layout = (FormTextLayout) getLayout(); 1652 if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) { 1653 size = layout.computeSize(this, wHint, hHint, changed); 1654 } else { 1655 size = new Point(wHint, hHint); 1656 } 1657 Rectangle trim = computeTrim(0, 0, size.x, size.y); 1658 if (DEBUG_TEXTSIZE) 1659 System.out.println("FormText Computed size: "+trim); return new Point(trim.width, trim.height); 1661 } 1662 1663 private void disposeResourceTable(boolean disposeBoldFont) { 1664 if (disposeBoldFont) { 1665 Font boldFont = (Font) resourceTable 1666 .get(FormTextModel.BOLD_FONT_ID); 1667 if (boldFont != null) { 1668 boldFont.dispose(); 1669 resourceTable.remove(FormTextModel.BOLD_FONT_ID); 1670 } 1671 } 1672 ArrayList imagesToRemove = new ArrayList (); 1673 for (Enumeration enm = resourceTable.keys(); enm.hasMoreElements();) { 1674 String key = (String ) enm.nextElement(); 1675 if (key.startsWith(ImageSegment.SEL_IMAGE_PREFIX)) { 1676 Object obj = resourceTable.get(key); 1677 if (obj instanceof Image) { 1678 Image image = (Image) obj; 1679 if (!image.isDisposed()) { 1680 image.dispose(); 1681 imagesToRemove.add(key); 1682 } 1683 } 1684 } 1685 } 1686 for (int i = 0; i < imagesToRemove.size(); i++) { 1687 resourceTable.remove(imagesToRemove.get(i)); 1688 } 1689 } 1690 1691 1696 public void setEnabled(boolean enabled) { 1697 super.setEnabled(enabled); 1698 redraw(); 1699 } 1700 1701 1704 public boolean setFocus() { 1705 mouseFocus = true; 1706 FormUtil.setFocusScrollingEnabled(this, false); 1707 boolean result = super.setFocus(); 1708 mouseFocus = false; 1709 FormUtil.setFocusScrollingEnabled(this, true); 1710 return result; 1711 } 1712} 1713 | Popular Tags |