1 11 package org.eclipse.jface.fieldassist; 12 13 import java.util.ArrayList ; 14 15 import org.eclipse.core.runtime.ListenerList; 16 import org.eclipse.jface.bindings.keys.KeyStroke; 17 import org.eclipse.jface.dialogs.PopupDialog; 18 import org.eclipse.core.runtime.Assert; 19 import org.eclipse.jface.viewers.ILabelProvider; 20 import org.eclipse.swt.SWT; 21 import org.eclipse.swt.events.DisposeEvent; 22 import org.eclipse.swt.events.DisposeListener; 23 import org.eclipse.swt.events.FocusAdapter; 24 import org.eclipse.swt.events.FocusEvent; 25 import org.eclipse.swt.events.SelectionEvent; 26 import org.eclipse.swt.events.SelectionListener; 27 import org.eclipse.swt.graphics.Image; 28 import org.eclipse.swt.graphics.Point; 29 import org.eclipse.swt.graphics.Rectangle; 30 import org.eclipse.swt.layout.GridData; 31 import org.eclipse.swt.widgets.Composite; 32 import org.eclipse.swt.widgets.Control; 33 import org.eclipse.swt.widgets.Event; 34 import org.eclipse.swt.widgets.Listener; 35 import org.eclipse.swt.widgets.ScrollBar; 36 import org.eclipse.swt.widgets.Shell; 37 import org.eclipse.swt.widgets.Table; 38 import org.eclipse.swt.widgets.TableItem; 39 import org.eclipse.swt.widgets.Text; 40 41 56 public class ContentProposalAdapter { 57 58 64 class ContentProposalPopup extends PopupDialog { 65 72 private final class PopupCloserListener implements Listener { 73 private boolean scrollbarClicked = false; 74 75 public void handleEvent(final Event e) { 76 77 if (e.type == SWT.FocusOut) { 80 scrollbarClicked = false; 81 87 e.display.asyncExec(new Runnable () { 88 public void run() { 89 if (isValid()) { 90 if (scrollbarClicked 91 || hasFocus() 92 || (infoPopup != null && infoPopup 93 .hasFocus())) { 94 return; 95 } 96 Shell activeShell = e.display.getActiveShell(); 102 if (activeShell == getShell() 103 || (infoPopup != null && infoPopup 104 .getShell() == activeShell)) { 105 return; 106 } 107 112 close(); 113 } 114 } 115 }); 116 return; 117 } 118 119 if (e.type == SWT.Selection) { 122 scrollbarClicked = true; 123 return; 124 } 125 close(); 127 } 128 129 void installListeners() { 132 proposalTable.addListener(SWT.FocusOut, this); 134 ScrollBar scrollbar = proposalTable.getVerticalBar(); 135 if (scrollbar != null) { 136 scrollbar.addListener(SWT.Selection, this); 137 } 138 139 getShell().addListener(SWT.Deactivate, this); 141 getShell().addListener(SWT.Close, this); 142 143 control.addListener(SWT.MouseDoubleClick, this); 145 control.addListener(SWT.MouseDown, this); 146 control.addListener(SWT.Dispose, this); 147 control.addListener(SWT.FocusOut, this); 148 Shell controlShell = control.getShell(); 150 controlShell.addListener(SWT.Move, this); 151 controlShell.addListener(SWT.Resize, this); 152 153 } 154 155 void removeListeners() { 157 if (isValid()) { 158 proposalTable.removeListener(SWT.FocusOut, this); 159 ScrollBar scrollbar = proposalTable.getVerticalBar(); 160 if (scrollbar != null) { 161 scrollbar.removeListener(SWT.Selection, this); 162 } 163 164 getShell().removeListener(SWT.Deactivate, this); 165 getShell().removeListener(SWT.Close, this); 166 } 167 168 if (control != null && !control.isDisposed()) { 169 170 control.removeListener(SWT.MouseDoubleClick, this); 171 control.removeListener(SWT.MouseDown, this); 172 control.removeListener(SWT.Dispose, this); 173 control.removeListener(SWT.FocusOut, this); 174 175 Shell controlShell = control.getShell(); 176 controlShell.removeListener(SWT.Move, this); 177 controlShell.removeListener(SWT.Resize, this); 178 } 179 } 180 } 181 182 185 private final class TargetControlListener implements Listener { 186 public void handleEvent(Event e) { 188 if (!isValid()) { 189 return; 190 } 191 192 char key = e.character; 193 194 if (e.type == SWT.Traverse) { 197 if (key != 0) { 203 e.doit = false; 204 return; 205 } 206 e.detail = SWT.TRAVERSE_NONE; 211 e.doit = true; 212 } else { 213 e.doit = propagateKeys; 216 } 217 218 220 if (key == 0) { 221 int newSelection = proposalTable.getSelectionIndex(); 222 int visibleRows = (proposalTable.getSize().y / proposalTable 223 .getItemHeight()) - 1; 224 switch (e.keyCode) { 225 case SWT.ARROW_UP: 226 newSelection -= 1; 227 if (newSelection < 0) { 228 newSelection = proposalTable.getItemCount() - 1; 229 } 230 if (e.type == SWT.KeyDown) { 233 e.doit = false; 235 } 236 237 break; 238 239 case SWT.ARROW_DOWN: 240 newSelection += 1; 241 if (newSelection > proposalTable.getItemCount() - 1) { 242 newSelection = 0; 243 } 244 if (e.type == SWT.KeyDown) { 247 e.doit = false; 249 } 250 251 break; 252 253 case SWT.PAGE_DOWN: 254 newSelection += visibleRows; 255 if (newSelection >= proposalTable.getItemCount()) { 256 newSelection = proposalTable.getItemCount() - 1; 257 } 258 if (e.type == SWT.KeyDown) { 259 e.doit = false; 261 } 262 break; 263 264 case SWT.PAGE_UP: 265 newSelection -= visibleRows; 266 if (newSelection < 0) { 267 newSelection = 0; 268 } 269 if (e.type == SWT.KeyDown) { 270 e.doit = false; 272 } 273 break; 274 275 case SWT.HOME: 276 newSelection = 0; 277 if (e.type == SWT.KeyDown) { 278 e.doit = false; 280 } 281 break; 282 283 case SWT.END: 284 newSelection = proposalTable.getItemCount() - 1; 285 if (e.type == SWT.KeyDown) { 286 e.doit = false; 288 } 289 break; 290 291 case SWT.ARROW_LEFT: 296 case SWT.ARROW_RIGHT: 297 if (e.type == SWT.Traverse) { 298 e.doit = false; 299 } else { 300 e.doit = true; 301 String contents = getControlContentAdapter() 302 .getControlContents(getControl()); 303 if (contents.length() > 0) { 309 asyncRecomputeProposals(filterText); 310 } 311 } 312 break; 313 314 default: 318 if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 319 && e.keyCode != SWT.MOD2 320 && e.keyCode != SWT.MOD3 321 && e.keyCode != SWT.MOD4) { 322 close(); 323 } 324 return; 325 } 326 327 if (newSelection >= 0) { 330 selectProposal(newSelection); 331 } 332 return; 333 } 334 335 switch (key) { 339 case SWT.ESC: 340 e.doit = false; 341 close(); 342 break; 343 344 case SWT.LF: 345 case SWT.CR: 346 e.doit = false; 347 Object p = getSelectedProposal(); 348 if (p != null) { 349 acceptCurrentProposal(); 350 } else { 351 close(); 352 } 353 break; 354 355 case SWT.TAB: 356 e.doit = false; 357 getShell().setFocus(); 358 return; 359 360 case SWT.BS: 361 if (filterStyle != FILTER_NONE) { 363 if (filterText.length() == 0) { 365 return; 366 } 367 filterText = filterText.substring(0, filterText 369 .length() - 1); 370 asyncRecomputeProposals(filterText); 371 return; 372 } 373 int pos = getControlContentAdapter().getCursorPosition( 378 getControl()); 379 if (pos > 0) { 384 asyncRecomputeProposals(filterText); 385 } 386 break; 387 388 default: 389 if (Character.isDefined(key)) { 393 if (filterStyle == FILTER_CUMULATIVE) { 394 filterText = filterText + String.valueOf(key); 395 } else if (filterStyle == FILTER_CHARACTER) { 396 filterText = String.valueOf(key); 397 } 398 asyncRecomputeProposals(filterText); 400 } 401 break; 402 } 403 } 404 } 405 406 409 private class InfoPopupDialog extends PopupDialog { 410 411 414 private Text text; 415 416 419 private String contents = EMPTY; 420 421 424 InfoPopupDialog(Shell parent) { 425 super(parent, PopupDialog.HOVER_SHELLSTYLE, false, false, 426 false, false, null, null); 427 } 428 429 432 protected Control createDialogArea(Composite parent) { 433 text = new Text(parent, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP 434 | SWT.NO_FOCUS); 435 436 GridData gd = new GridData(GridData.BEGINNING 438 | GridData.FILL_BOTH); 439 gd.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING; 440 gd.verticalIndent = PopupDialog.POPUP_VERTICALSPACING; 441 text.setLayoutData(gd); 442 text.setText(contents); 443 444 text.addFocusListener(new FocusAdapter() { 446 public void focusGained(FocusEvent event) { 447 ContentProposalPopup.this.close(); 448 } 449 }); 450 return text; 451 } 452 453 456 protected void adjustBounds() { 457 Rectangle parentBounds = getParentShell().getBounds(); 458 Rectangle proposedBounds; 459 Rectangle rightProposedBounds = new Rectangle(parentBounds.x 461 + parentBounds.width 462 + PopupDialog.POPUP_HORIZONTALSPACING, parentBounds.y 463 + PopupDialog.POPUP_VERTICALSPACING, 464 parentBounds.width, parentBounds.height); 465 rightProposedBounds = getConstrainedShellBounds(rightProposedBounds); 466 if (rightProposedBounds.intersects(parentBounds)) { 468 Rectangle leftProposedBounds = new Rectangle(parentBounds.x 469 - parentBounds.width - POPUP_HORIZONTALSPACING - 1, 470 parentBounds.y, parentBounds.width, 471 parentBounds.height); 472 leftProposedBounds = getConstrainedShellBounds(leftProposedBounds); 473 if (leftProposedBounds.intersects(parentBounds)) { 476 if (rightProposedBounds.x - parentBounds.x >= parentBounds.x 477 - leftProposedBounds.x) { 478 rightProposedBounds.x = parentBounds.x 479 + parentBounds.width 480 + PopupDialog.POPUP_HORIZONTALSPACING; 481 proposedBounds = rightProposedBounds; 482 } else { 483 leftProposedBounds.width = parentBounds.x 484 - POPUP_HORIZONTALSPACING 485 - leftProposedBounds.x; 486 proposedBounds = leftProposedBounds; 487 } 488 } else { 489 proposedBounds = leftProposedBounds; 491 } 492 } else { 493 proposedBounds = rightProposedBounds; 495 } 496 getShell().setBounds(proposedBounds); 497 } 498 499 502 void setContents(String newContents) { 503 if (newContents == null) { 504 newContents = EMPTY; 505 } 506 this.contents = newContents; 507 if (text != null && !text.isDisposed()) { 508 text.setText(contents); 509 } 510 } 511 512 515 boolean hasFocus() { 516 if (text == null || text.isDisposed()) { 517 return false; 518 } 519 return text.getShell().isFocusControl() 520 || text.isFocusControl(); 521 } 522 } 523 524 527 private Listener targetControlListener; 528 529 532 private PopupCloserListener popupCloser; 533 534 537 private Table proposalTable; 538 539 542 private IContentProposal[] proposals; 543 544 548 private InfoPopupDialog infoPopup; 549 550 553 private boolean pendingDescriptionUpdate = false; 554 555 559 private String filterText = EMPTY; 560 561 570 ContentProposalPopup(String infoText, IContentProposal[] proposals) { 571 super(control.getShell(), SWT.RESIZE | SWT.ON_TOP, false, false, 579 false, false, null, infoText); 580 this.proposals = proposals; 581 } 582 583 589 protected Control createContents(Composite parent) { 590 Control contents = super.createContents(parent); 591 changeDefaultColors(parent); 592 return contents; 593 } 594 595 598 private void changeDefaultColors(Control control) { 599 applyForegroundColor(getShell().getDisplay().getSystemColor( 600 SWT.COLOR_LIST_FOREGROUND), control); 601 applyBackgroundColor(getShell().getDisplay().getSystemColor( 602 SWT.COLOR_LIST_BACKGROUND), control); 603 } 604 605 613 protected final Control createDialogArea(final Composite parent) { 614 if (USE_VIRTUAL) { 616 proposalTable = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL 617 | SWT.VIRTUAL); 618 619 Listener listener = new Listener() { 620 public void handleEvent(Event event) { 621 handleSetData(event); 622 } 623 }; 624 proposalTable.addListener(SWT.SetData, listener); 625 } else { 626 proposalTable = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL); 627 } 628 629 setProposals(filterProposals(proposals, filterText)); 631 632 proposalTable.setHeaderVisible(false); 633 proposalTable.addSelectionListener(new SelectionListener() { 634 635 public void widgetSelected(SelectionEvent e) { 636 if (e.item == null) { 639 if (infoPopup != null) { 640 infoPopup.close(); 641 } 642 } else { 643 showProposalDescription(); 644 } 645 } 646 647 public void widgetDefaultSelected(SelectionEvent e) { 649 acceptCurrentProposal(); 650 } 651 }); 652 return proposalTable; 653 } 654 655 660 protected void adjustBounds() { 661 Point location = control.getDisplay().map(control.getParent(), 663 null, control.getLocation()); 664 int initialX = location.x + POPUP_OFFSET; 665 int initialY = location.y + control.getSize().y + POPUP_OFFSET; 666 if (getProposalAcceptanceStyle() == PROPOSAL_INSERT) { 669 Rectangle insertionBounds = controlContentAdapter 670 .getInsertionBounds(control); 671 initialX = initialX + insertionBounds.x; 672 initialY = location.y + insertionBounds.y 673 + insertionBounds.height; 674 } 675 676 if (popupSize == null) { 679 GridData data = new GridData(GridData.FILL_BOTH); 680 data.heightHint = proposalTable.getItemHeight() 681 * POPUP_CHAR_HEIGHT; 682 data.widthHint = Math.max(control.getSize().x, 683 POPUP_MINIMUM_WIDTH); 684 proposalTable.setLayoutData(data); 685 getShell().pack(); 686 popupSize = getShell().getSize(); 687 } 688 getShell().setBounds(initialX, initialY, popupSize.x, popupSize.y); 689 690 getShell().addListener(SWT.Resize, new Listener() { 692 public void handleEvent(Event e) { 693 popupSize = getShell().getSize(); 694 if (infoPopup != null) { 695 infoPopup.adjustBounds(); 696 } 697 } 698 }); 699 } 700 701 705 private void handleSetData(Event event) { 706 TableItem item = (TableItem) event.item; 707 int index = proposalTable.indexOf(item); 708 709 if (0 <= index && index < proposals.length) { 710 IContentProposal current = proposals[index]; 711 item.setText(getString(current)); 712 item.setImage(getImage(current)); 713 item.setData(current); 714 } else { 715 } 717 } 718 719 723 private void setProposals(IContentProposal[] newProposals) { 724 if (newProposals == null || newProposals.length == 0) { 725 newProposals = getEmptyProposalArray(); 726 } 727 this.proposals = newProposals; 728 729 if (isValid()) { 731 final int newSize = newProposals.length; 732 if (USE_VIRTUAL) { 733 proposalTable.setItemCount(newSize); 736 proposalTable.clearAll(); 737 } else { 738 proposalTable.setRedraw(false); 740 proposalTable.setItemCount(newSize); 741 TableItem[] items = proposalTable.getItems(); 742 for (int i = 0; i < items.length; i++) { 743 TableItem item = items[i]; 744 IContentProposal proposal = newProposals[i]; 745 item.setText(getString(proposal)); 746 item.setImage(getImage(proposal)); 747 item.setData(proposal); 748 } 749 proposalTable.setRedraw(true); 750 } 751 if (newProposals.length > 0) { 753 selectProposal(0); 754 } else { 755 if (infoPopup != null) { 757 infoPopup.close(); 758 } 759 760 } 761 } 762 } 763 764 768 private String getString(IContentProposal proposal) { 769 if (proposal == null) { 770 return EMPTY; 771 } 772 if (labelProvider == null) { 773 return proposal.getLabel() == null ? proposal.getContent() 774 : proposal.getLabel(); 775 } 776 return labelProvider.getText(proposal); 777 } 778 779 783 private Image getImage(IContentProposal proposal) { 784 if (proposal == null || labelProvider == null) { 785 return null; 786 } 787 return labelProvider.getImage(proposal); 788 } 789 790 794 private IContentProposal[] getEmptyProposalArray() { 795 return new IContentProposal[0]; 796 } 797 798 802 private boolean isValid() { 803 return proposalTable != null && !proposalTable.isDisposed(); 804 } 805 806 809 private boolean hasFocus() { 810 if (!isValid()) { 811 return false; 812 } 813 return getShell().isFocusControl() 814 || proposalTable.isFocusControl(); 815 } 816 817 820 private IContentProposal getSelectedProposal() { 821 if (isValid()) { 822 int i = proposalTable.getSelectionIndex(); 823 if (proposals == null || i < 0 || i >= proposals.length) { 824 return null; 825 } 826 return proposals[i]; 827 } 828 return null; 829 } 830 831 834 private void selectProposal(int index) { 835 Assert 836 .isTrue(index >= 0, 837 "Proposal index should never be negative"); if (!isValid() || proposals == null || index >= proposals.length) { 839 return; 840 } 841 proposalTable.setSelection(index); 842 proposalTable.showSelection(); 843 844 showProposalDescription(); 845 } 846 847 856 public int open() { 857 int value = super.open(); 858 if (popupCloser == null) { 859 popupCloser = new PopupCloserListener(); 860 } 861 popupCloser.installListeners(); 862 IContentProposal p = getSelectedProposal(); 863 if (p != null) { 864 showProposalDescription(); 865 } 866 return value; 867 } 868 869 876 public boolean close() { 877 popupCloser.removeListeners(); 878 if (infoPopup != null) { 879 infoPopup.close(); 880 } 881 boolean ret = super.close(); 882 notifyPopupClosed(); 883 return ret; 884 } 885 886 890 private void showProposalDescription() { 891 if (!pendingDescriptionUpdate) { 894 Runnable runnable = new Runnable () { 899 public void run() { 900 pendingDescriptionUpdate = true; 901 try { 902 Thread.sleep(POPUP_DELAY); 903 } catch (InterruptedException e) { 904 } 905 if (!isValid()) { 906 return; 907 } 908 getShell().getDisplay().syncExec(new Runnable () { 909 public void run() { 910 IContentProposal p = getSelectedProposal(); 913 if (p != null) { 914 String description = p.getDescription(); 915 if (description != null) { 916 if (infoPopup == null) { 917 infoPopup = new InfoPopupDialog( 918 getShell()); 919 infoPopup.open(); 920 infoPopup 921 .getShell() 922 .addDisposeListener( 923 new DisposeListener() { 924 public void widgetDisposed( 925 DisposeEvent event) { 926 infoPopup = null; 927 } 928 }); 929 } 930 infoPopup.setContents(p 931 .getDescription()); 932 } else if (infoPopup != null) { 933 infoPopup.close(); 934 } 935 pendingDescriptionUpdate = false; 936 937 } 938 } 939 }); 940 } 941 }; 942 Thread t = new Thread (runnable); 943 t.start(); 944 } 945 } 946 947 950 private void acceptCurrentProposal() { 951 IContentProposal proposal = getSelectedProposal(); 957 close(); 958 proposalAccepted(proposal); 959 } 960 961 965 private void recomputeProposals(String filterText) { 966 IContentProposal[] allProposals = getProposals(); 967 if (allProposals.length == 0) { 971 proposals = allProposals; 972 close(); 973 } else { 974 setProposals(filterProposals(allProposals, filterText)); 976 } 977 } 978 979 985 private void asyncRecomputeProposals(final String filterText) { 986 if (isValid()) { 987 control.getDisplay().asyncExec(new Runnable () { 988 public void run() { 989 recordCursorPosition(); 990 recomputeProposals(filterText); 991 } 992 }); 993 } else { 994 recomputeProposals(filterText); 995 } 996 } 997 998 1002 private IContentProposal[] filterProposals( 1003 IContentProposal[] proposals, String filterString) { 1004 if (filterString.length() == 0) { 1005 return proposals; 1006 } 1007 1008 ArrayList list = new ArrayList (); 1011 for (int i = 0; i < proposals.length; i++) { 1012 String string = getString(proposals[i]); 1013 if (string.length() >= filterString.length() 1014 && string.substring(0, filterString.length()) 1015 .equalsIgnoreCase(filterString)) { 1016 list.add(proposals[i]); 1017 } 1018 1019 } 1020 return (IContentProposal[]) list.toArray(new IContentProposal[list 1021 .size()]); 1022 } 1023 1024 Listener getTargetControlListener() { 1025 if (targetControlListener == null) { 1026 targetControlListener = new TargetControlListener(); 1027 } 1028 return targetControlListener; 1029 } 1030 } 1031 1032 1035 public static final boolean DEBUG = false; 1036 1037 1040 public static final int PROPOSAL_INSERT = 1; 1041 1042 1046 public static final int PROPOSAL_REPLACE = 2; 1047 1048 1055 public static final int PROPOSAL_IGNORE = 3; 1056 1057 1061 public static final int FILTER_NONE = 1; 1062 1063 1067 public static final int FILTER_CHARACTER = 2; 1068 1069 1073 public static final int FILTER_CUMULATIVE = 3; 1074 1075 1081 private static final boolean USE_VIRTUAL = !"motif".equals(SWT.getPlatform()); 1083 1086 private static final int POPUP_DELAY = 750; 1087 1088 1092 private static final int POPUP_CHAR_HEIGHT = 10; 1093 1094 1098 private static final int POPUP_MINIMUM_WIDTH = 300; 1099 1100 1103 private static final int POPUP_OFFSET = 3; 1104 1105 1108 private static final String EMPTY = ""; 1110 1113 private IContentProposalProvider proposalProvider; 1114 1115 1119 private ILabelProvider labelProvider; 1120 1121 1124 private Control control; 1125 1126 1130 private IControlContentAdapter controlContentAdapter; 1131 1132 1135 private ContentProposalPopup popup; 1136 1137 1140 private KeyStroke triggerKeyStroke; 1141 1142 1145 private String autoActivateString; 1146 1147 1152 private int proposalAcceptanceStyle = PROPOSAL_INSERT; 1153 1154 1159 private boolean propagateKeys = true; 1160 1161 1165 private int filterStyle = FILTER_NONE; 1166 1167 1170 private Listener controlListener; 1171 1172 1175 private ListenerList proposalListeners = new ListenerList(); 1176 1177 1180 private ListenerList proposalListeners2 = new ListenerList(); 1181 1182 1186 private boolean isEnabled = true; 1187 1188 1191 private int autoActivationDelay = 0; 1192 1193 1197 private boolean receivedKeyDown; 1198 1199 1202 private Point popupSize; 1203 1204 1209 private int insertionPos = -1; 1210 1211 1215 private boolean modifyingControlContent = false; 1216 1217 1248 public ContentProposalAdapter(Control control, 1249 IControlContentAdapter controlContentAdapter, 1250 IContentProposalProvider proposalProvider, KeyStroke keyStroke, 1251 char[] autoActivationCharacters) { 1252 super(); 1253 Assert.isNotNull(control); 1255 Assert.isNotNull(controlContentAdapter); 1256 this.control = control; 1257 this.controlContentAdapter = controlContentAdapter; 1258 1259 this.proposalProvider = proposalProvider; 1261 this.triggerKeyStroke = keyStroke; 1262 if (autoActivationCharacters != null) { 1263 this.autoActivateString = new String (autoActivationCharacters); 1264 } 1265 addControlListener(control); 1266 } 1267 1268 1273 public Control getControl() { 1274 return control; 1275 } 1276 1277 1283 public ILabelProvider getLabelProvider() { 1284 return labelProvider; 1285 } 1286 1287 1293 public boolean isEnabled() { 1294 return isEnabled; 1295 } 1296 1297 1305 public void setLabelProvider(ILabelProvider labelProvider) { 1306 this.labelProvider = labelProvider; 1307 } 1308 1309 1317 public IContentProposalProvider getContentProposalProvider() { 1318 return proposalProvider; 1319 } 1320 1321 1327 public void setContentProposalProvider( 1328 IContentProposalProvider proposalProvider) { 1329 this.proposalProvider = proposalProvider; 1330 } 1331 1332 1344 public char[] getAutoActivationCharacters() { 1345 if (autoActivateString == null) { 1346 return null; 1347 } 1348 return autoActivateString.toCharArray(); 1349 } 1350 1351 1367 public void setAutoActivationCharacters(char[] autoActivationCharacters) { 1368 if (autoActivationCharacters == null) { 1369 this.autoActivateString = null; 1370 } else { 1371 this.autoActivateString = new String (autoActivationCharacters); 1372 } 1373 } 1374 1375 1382 public int getAutoActivationDelay() { 1383 return autoActivationDelay; 1384 1385 } 1386 1387 1394 public void setAutoActivationDelay(int delay) { 1395 autoActivationDelay = delay; 1396 1397 } 1398 1399 1408 public int getProposalAcceptanceStyle() { 1409 return proposalAcceptanceStyle; 1410 } 1411 1412 1422 public void setProposalAcceptanceStyle(int acceptance) { 1423 proposalAcceptanceStyle = acceptance; 1424 } 1425 1426 1440 public int getFilterStyle() { 1441 return filterStyle; 1442 } 1443 1444 1466 public void setFilterStyle(int filterStyle) { 1467 this.filterStyle = filterStyle; 1468 } 1469 1470 1476 public Point getPopupSize() { 1477 return popupSize; 1478 } 1479 1480 1488 public void setPopupSize(Point size) { 1489 popupSize = size; 1490 } 1491 1492 1503 public boolean getPropagateKeys() { 1504 return propagateKeys; 1505 } 1506 1507 1518 public void setPropagateKeys(boolean propagateKeys) { 1519 this.propagateKeys = propagateKeys; 1520 } 1521 1522 1531 public IControlContentAdapter getControlContentAdapter() { 1532 return controlContentAdapter; 1533 } 1534 1535 1544 public void setEnabled(boolean enabled) { 1545 if (isEnabled && !enabled) { 1548 if (popup != null) { 1549 popup.close(); 1550 } 1551 } 1552 isEnabled = enabled; 1553 } 1554 1555 1568 public void addContentProposalListener(IContentProposalListener listener) { 1569 proposalListeners.add(listener); 1570 } 1571 1572 1585 public void removeContentProposalListener(IContentProposalListener listener) { 1586 proposalListeners.remove(listener); 1587 } 1588 1589 1603 public void addContentProposalListener(IContentProposalListener2 listener) { 1604 proposalListeners2.add(listener); 1605 } 1606 1607 1620 public void removeContentProposalListener(IContentProposalListener2 listener) { 1621 proposalListeners2.remove(listener); 1622 } 1623 1624 1628 private void addControlListener(Control control) { 1629 if (DEBUG) { 1630 System.out 1631 .println("ContentProposalListener#installControlListener()"); } 1633 1634 if (controlListener != null) { 1635 return; 1636 } 1637 controlListener = new Listener() { 1638 public void handleEvent(Event e) { 1639 if (!isEnabled) { 1640 return; 1641 } 1642 1643 switch (e.type) { 1644 case SWT.Traverse: 1645 case SWT.KeyDown: 1646 if (DEBUG) { 1647 StringBuffer sb; 1648 if (e.type == SWT.Traverse) { 1649 sb = new StringBuffer ("Traverse"); } else { 1651 sb = new StringBuffer ("KeyDown"); } 1653 sb.append(" received by adapter"); dump(sb.toString(), e); 1655 } 1656 if (popup != null) { 1659 popup.getTargetControlListener().handleEvent(e); 1660 if (DEBUG) { 1661 StringBuffer sb; 1662 if (e.type == SWT.Traverse) { 1663 sb = new StringBuffer ("Traverse"); } else { 1665 sb = new StringBuffer ("KeyDown"); } 1667 sb.append(" after being handled by popup"); dump(sb.toString(), e); 1669 } 1670 1671 return; 1672 } 1673 1674 if (e.type == SWT.Traverse) { 1676 return; 1677 } 1678 1679 if (triggerKeyStroke != null) { 1682 if ((triggerKeyStroke.getModifierKeys() == KeyStroke.NO_KEY && triggerKeyStroke 1685 .getNaturalKey() == e.character) 1686 || 1687 (triggerKeyStroke.getNaturalKey() == e.keyCode && ((triggerKeyStroke 1690 .getModifierKeys() & e.stateMask) == triggerKeyStroke 1691 .getModifierKeys()))) { 1692 e.doit = false; 1695 openProposalPopup(false); 1696 return; 1697 } 1698 } 1699 1703 if (e.character != 0) { 1704 if (autoActivateString != null) { 1707 if (autoActivateString.indexOf(e.character) >= 0) { 1708 e.doit = propagateKeys; 1709 autoActivate(); 1710 } 1711 } else { 1712 receivedKeyDown = true; 1718 } 1719 } 1720 break; 1721 1722 case SWT.Modify: 1733 if (triggerKeyStroke == null && autoActivateString == null 1734 && !modifyingControlContent) { 1735 if (DEBUG) { 1736 dump("Modify event triggers autoactivation", e); } 1738 autoActivate(); 1739 } 1740 break; 1741 default: 1742 break; 1743 } 1744 } 1745 1746 1754 private void dump(String who, Event e) { 1755 StringBuffer sb = new StringBuffer ( 1756 "--- [ContentProposalAdapter]\n"); sb.append(who); 1758 sb.append(" - e: keyCode=" + e.keyCode + hex(e.keyCode)); sb.append("; character=" + e.character + hex(e.character)); sb.append("; stateMask=" + e.stateMask + hex(e.stateMask)); sb.append("; doit=" + e.doit); sb.append("; detail=" + e.detail + hex(e.detail)); sb.append("; widget=" + e.widget); System.out.println(sb); 1765 } 1766 1767 private String hex(int i) { 1768 return "[0x" + Integer.toHexString(i) + ']'; } 1770 }; 1771 control.addListener(SWT.KeyDown, controlListener); 1772 control.addListener(SWT.Traverse, controlListener); 1773 control.addListener(SWT.Modify, controlListener); 1774 1775 if (DEBUG) { 1776 System.out 1777 .println("ContentProposalAdapter#installControlListener() - installed"); } 1779 } 1780 1781 1791 private void openProposalPopup(boolean autoActivated) { 1792 if (isValid()) { 1793 if (popup == null) { 1794 recordCursorPosition(); IContentProposal[] proposals = getProposals(); 1797 if (proposals.length > 0) { 1798 if (DEBUG) { 1799 System.out.println("POPUP OPENED BY PRECEDING EVENT"); } 1801 recordCursorPosition(); 1802 popup = new ContentProposalPopup(null, proposals); 1803 popup.open(); 1804 popup.getShell().addDisposeListener(new DisposeListener() { 1805 public void widgetDisposed(DisposeEvent event) { 1806 popup = null; 1807 } 1808 }); 1809 notifyPopupOpened(); 1810 } else if (!autoActivated) { 1811 getControl().getDisplay().beep(); 1812 } 1813 } 1814 } 1815 } 1816 1817 1824 protected void openProposalPopup() { 1825 openProposalPopup(false); 1826 } 1827 1828 1836 protected void closeProposalPopup() { 1837 if (popup != null) { 1838 popup.close(); 1839 } 1840 } 1841 1842 1848 private void proposalAccepted(IContentProposal proposal) { 1849 switch (proposalAcceptanceStyle) { 1850 case (PROPOSAL_REPLACE): 1851 setControlContent(proposal.getContent(), proposal 1852 .getCursorPosition()); 1853 break; 1854 case (PROPOSAL_INSERT): 1855 insertControlContent(proposal.getContent(), proposal 1856 .getCursorPosition()); 1857 break; 1858 default: 1859 break; 1862 } 1863 1864 notifyProposalAccepted(proposal); 1866 } 1867 1868 1872 private void setControlContent(String text, int cursorPosition) { 1873 if (isValid()) { 1874 modifyingControlContent = true; 1876 1877 controlContentAdapter.setControlContents(control, text, 1878 cursorPosition); 1879 1880 modifyingControlContent = false; 1881 } 1882 } 1883 1884 1888 private void insertControlContent(String text, int cursorPosition) { 1889 if (isValid()) { 1890 modifyingControlContent = true; 1892 if (insertionPos != -1) { 1897 controlContentAdapter.setCursorPosition(control, insertionPos); 1898 } 1899 controlContentAdapter.insertControlContents(control, text, 1900 cursorPosition); 1901 modifyingControlContent = false; 1902 } 1903 } 1904 1905 1908 private boolean isValid() { 1909 return control != null && !control.isDisposed() 1910 && controlContentAdapter != null; 1911 } 1912 1913 1916 private void recordCursorPosition() { 1917 if (isValid()) { 1918 insertionPos = getControlContentAdapter() 1919 .getCursorPosition(control); 1920 1921 } 1922 } 1923 1924 1928 private IContentProposal[] getProposals() { 1929 if (proposalProvider == null || !isValid()) { 1930 return null; 1931 } 1932 if (DEBUG) { 1933 System.out.println(">>> obtaining proposals from provider"); } 1935 int position = insertionPos; 1936 if (position == -1) { 1937 position = getControlContentAdapter().getCursorPosition( 1938 getControl()); 1939 } 1940 String contents = getControlContentAdapter().getControlContents( 1941 getControl()); 1942 IContentProposal[] proposals = proposalProvider.getProposals(contents, 1943 position); 1944 return proposals; 1945 } 1946 1947 1951 private void autoActivate() { 1952 if (autoActivationDelay > 0) { 1953 Runnable runnable = new Runnable () { 1954 public void run() { 1955 receivedKeyDown = false; 1956 try { 1957 Thread.sleep(autoActivationDelay); 1958 } catch (InterruptedException e) { 1959 } 1960 if (!isValid() || receivedKeyDown) { 1961 return; 1962 } 1963 getControl().getDisplay().syncExec(new Runnable () { 1964 public void run() { 1965 openProposalPopup(true); 1966 } 1967 }); 1968 } 1969 }; 1970 Thread t = new Thread (runnable); 1971 t.start(); 1972 } else { 1973 getControl().getDisplay().asyncExec(new Runnable () { 1980 public void run() { 1981 if (isValid()) { 1982 openProposalPopup(true); 1983 } 1984 } 1985 }); 1986 } 1987 } 1988 1989 1992 private void notifyProposalAccepted(IContentProposal proposal) { 1993 if (DEBUG) { 1994 System.out.println("Notify listeners - proposal accepted."); } 1996 final Object [] listenerArray = proposalListeners.getListeners(); 1997 for (int i = 0; i < listenerArray.length; i++) { 1998 ((IContentProposalListener) listenerArray[i]) 1999 .proposalAccepted(proposal); 2000 } 2001 } 2002 2003 2006 private void notifyPopupOpened() { 2007 if (DEBUG) { 2008 System.out.println("Notify listeners - popup opened."); } 2010 final Object [] listenerArray = proposalListeners2.getListeners(); 2011 for (int i = 0; i < listenerArray.length; i++) { 2012 ((IContentProposalListener2) listenerArray[i]) 2013 .proposalPopupOpened(this); 2014 } 2015 } 2016 2017 2020 private void notifyPopupClosed() { 2021 if (DEBUG) { 2022 System.out.println("Notify listeners - popup closed."); } 2024 final Object [] listenerArray = proposalListeners2.getListeners(); 2025 for (int i = 0; i < listenerArray.length; i++) { 2026 ((IContentProposalListener2) listenerArray[i]) 2027 .proposalPopupClosed(this); 2028 } 2029 } 2030} 2031 | Popular Tags |