JFace/SWT content assist cell editor implementation
1 2 import java.text.MessageFormat; 3 4 import org.eclipse.jface.fieldassist.IContentProposalProvider; 5 import org.eclipse.jface.fieldassist.TextContentAdapter; 6 import org.eclipse.jface.fieldassist.TextControlCreator; 7 import org.eclipse.jface.util.Assert; 8 import org.eclipse.jface.viewers.CellEditor; 9 import org.eclipse.swt.SWT; 10 import org.eclipse.swt.events.FocusAdapter; 11 import org.eclipse.swt.events.FocusEvent; 12 import org.eclipse.swt.events.KeyAdapter; 13 import org.eclipse.swt.events.KeyEvent; 14 import org.eclipse.swt.events.ModifyEvent; 15 import org.eclipse.swt.events.ModifyListener; 16 import org.eclipse.swt.events.MouseAdapter; 17 import org.eclipse.swt.events.MouseEvent; 18 import org.eclipse.swt.events.SelectionAdapter; 19 import org.eclipse.swt.events.SelectionEvent; 20 import org.eclipse.swt.events.TraverseEvent; 21 import org.eclipse.swt.events.TraverseListener; 22 import org.eclipse.swt.widgets.Composite; 23 import org.eclipse.swt.widgets.Control; 24 import org.eclipse.swt.widgets.Text; 25 import org.eclipse.ui.fieldassist.ContentAssistField; 26 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; 27 28 /** 29 * A cell editor that manages a content assist field. 30 * The cell editor's value is the text string itself. 31 * 32 * This class may be instantiated; it is not intended to be subclassed. 33 * 34 */ 35 public class ContentAssistFieldCellEditor extends CellEditor 36 { 37 protected ContentAssistField field; 38 39 protected IContentProposalProvider contentProposalProvider; 40 41 protected char[] completionProposalAutoActivationCharacters; 42 43 private ModifyListener modifyListener; 44 45 public ContentAssistFieldCellEditor( char[] completionProposalAutoActivationCharacters, IContentProposalProvider contentProposalProvider) 46 { 47 super(); 48 this.completionProposalAutoActivationCharacters = completionProposalAutoActivationCharacters; 49 this.contentProposalProvider = contentProposalProvider; 50 } 51 52 /** 53 * Return the modify listener. 54 */ 55 private ModifyListener getModifyListener() { 56 if (modifyListener == null) { 57 modifyListener = new ModifyListener() { 58 public void modifyText(ModifyEvent e) { 59 editOccured(e); 60 } 61 }; 62 } 63 return modifyListener; 64 } 65 66 public ContentAssistFieldCellEditor(Composite parent, int style, char[] completionProposalAutoActivationCharacters, IContentProposalProvider contentProposalProvider) { 67 super( parent, style); 68 } 69 70 public ContentAssistFieldCellEditor(Composite parent,char[] completionProposalAutoActivationCharacters, IContentProposalProvider contentProposalProvider) 71 { 72 super(parent); 73 } 74 75 /** 76 * State information for updating action enablement 77 */ 78 private boolean isSelection = false; 79 80 private boolean isDeleteable = false; 81 82 private boolean isSelectable = false; 83 84 private Text text; 85 86 @Override 87 protected Control createControl(Composite parent) 88 { 89 //SimpleContentProposalProvider proposelProvider = new SimpleContentProposalProvider( new String[] { "a", "b", "c"}); 90 91 field = new ContentAssistField( 92 parent, 93 SWT.SINGLE, 94 new TextControlCreator(), 95 new TextContentAdapter(), 96 contentProposalProvider, 97 ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, 98 completionProposalAutoActivationCharacters 99 ); 100 101 text = (Text)field.getControl(); 102 text.addSelectionListener(new SelectionAdapter() { 103 public void widgetDefaultSelected(SelectionEvent e) { 104 handleDefaultSelection(e); 105 } 106 }); 107 108 text.addKeyListener(new KeyAdapter() { 109 // hook key pressed - see PR 14201 110 public void keyPressed(KeyEvent e) { 111 keyReleaseOccured(e); 112 113 // as a result of processing the above call, clients may have 114 // disposed this cell editor 115 if ((getControl() == null) || getControl().isDisposed()) { 116 return; 117 } 118 checkSelection(); // see explaination below 119 checkDeleteable(); 120 checkSelectable(); 121 } 122 }); 123 text.addTraverseListener(new TraverseListener() { 124 public void keyTraversed(TraverseEvent e) { 125 if (e.detail == SWT.TRAVERSE_ESCAPE 126 || e.detail == SWT.TRAVERSE_RETURN) { 127 e.doit = false; 128 } 129 } 130 }); 131 // We really want a selection listener but it is not supported so we 132 // use a key listener and a mouse listener to know when selection changes 133 // may have occured 134 text.addMouseListener(new MouseAdapter() { 135 public void mouseUp(MouseEvent e) { 136 checkSelection(); 137 checkDeleteable(); 138 checkSelectable(); 139 } 140 }); 141 text.addFocusListener(new FocusAdapter() { 142 public void focusLost(FocusEvent e) { 143 ContentAssistFieldCellEditor.this.focusLost(); 144 } 145 }); 146 text.setFont(parent.getFont()); 147 text.setBackground(parent.getBackground()); 148 text.setText("");//$NON-NLS-1$ 149 text.addModifyListener( getModifyListener()); 150 151 return field.getLayoutControl(); 152 } 153 154 /** 155 * Checks to see if the "deleteable" state (can delete/ 156 * nothing to delete) has changed and if so fire an 157 * enablement changed notification. 158 */ 159 private void checkDeleteable() { 160 boolean oldIsDeleteable = isDeleteable; 161 isDeleteable = isDeleteEnabled(); 162 if (oldIsDeleteable != isDeleteable) { 163 fireEnablementChanged(DELETE); 164 } 165 } 166 167 /** 168 * Checks to see if the "selectable" state (can select) 169 * has changed and if so fire an enablement changed notification. 170 */ 171 private void checkSelectable() { 172 boolean oldIsSelectable = isSelectable; 173 isSelectable = isSelectAllEnabled(); 174 if (oldIsSelectable != isSelectable) { 175 fireEnablementChanged(SELECT_ALL); 176 } 177 } 178 179 /** 180 * Checks to see if the selection state (selection / 181 * no selection) has changed and if so fire an 182 * enablement changed notification. 183 */ 184 private void checkSelection() { 185 boolean oldIsSelection = isSelection; 186 isSelection = text.getSelectionCount() > 0; 187 if (oldIsSelection != isSelection) { 188 fireEnablementChanged(COPY); 189 fireEnablementChanged(CUT); 190 } 191 } 192 193 /** 194 * Processes a modify event that occurred in this text cell editor. 195 * This framework method performs validation and sets the error message 196 * accordingly, and then reports a change via fireEditorValueChanged. 197 * Subclasses should call this method at appropriate times. Subclasses 198 * may extend or reimplement. 199 * 200 * @param e the SWT modify event 201 */ 202 protected void editOccured(ModifyEvent e) { 203 String value = text.getText(); 204 if (value == null) { 205 value = "";//$NON-NLS-1$ 206 } 207 Object typedValue = value; 208 boolean oldValidState = isValueValid(); 209 boolean newValidState = isCorrect(typedValue); 210 if (typedValue == null && newValidState) { 211 Assert.isTrue(false, 212 "Validator isn't limiting the cell editor's type range");//$NON-NLS-1$ 213 } 214 if (!newValidState) { 215 // try to insert the current value into the error message. 216 setErrorMessage(MessageFormat.format(getErrorMessage(), 217 new Object[] { value })); 218 } 219 valueChanged(oldValidState, newValidState); 220 } 221 222 /** 223 * Handles a default selection event from the text control by applying the editor 224 * value and deactivating this cell editor. 225 * 226 * @param event the selection event 227 */ 228 protected void handleDefaultSelection(SelectionEvent event) 229 { 230 // same with enter-key handling code in keyReleaseOccured(e); 231 fireApplyEditorValue(); 232 deactivate(); 233 } 234 235 @Override 236 protected void doSetFocus() 237 { 238 if( field!= null) { 239 field.getControl().setFocus(); 240 } 241 } 242 243 @Override 244 protected Object doGetValue() 245 { 246 return ((Text)field.getControl()).getText(); 247 } 248 249 @Override 250 protected void doSetValue(Object value) 251 { 252 ((Text)field.getControl()).setText( value.toString()); 253 } 254 255 /** 256 * The implementation of this 257 * method copies the 258 * current selection to the clipboard. 259 */ 260 public void performCopy() { 261 text.copy(); 262 } 263 264 /** 265 * The implementation of this 266 * method cuts the 267 * current selection to the clipboard. 268 */ 269 public void performCut() { 270 text.cut(); 271 checkSelection(); 272 checkDeleteable(); 273 checkSelectable(); 274 } 275 276 /** 277 * The implementation of this 278 * method deletes the 279 * current selection or, if there is no selection, 280 * the character next character from the current position. 281 */ 282 public void performDelete() { 283 if (text.getSelectionCount() > 0) { 284 // remove the contents of the current selection 285 text.insert(""); //$NON-NLS-1$ 286 } else { 287 // remove the next character 288 int pos = text.getCaretPosition(); 289 if (pos < text.getCharCount()) { 290 text.setSelection(pos, pos + 1); 291 text.insert(""); //$NON-NLS-1$ 292 } 293 } 294 checkSelection(); 295 checkDeleteable(); 296 checkSelectable(); 297 } 298 299 /** 300 * The implementation of this 301 * method pastes the 302 * the clipboard contents over the current selection. 303 */ 304 public void performPaste() { 305 text.paste(); 306 checkSelection(); 307 checkDeleteable(); 308 checkSelectable(); 309 } 310 311 /** 312 * The implementation of this 313 * method selects all of the 314 * current text. 315 */ 316 public void performSelectAll() { 317 text.selectAll(); 318 checkSelection(); 319 checkDeleteable(); 320 } 321 322 323 324 /** 325 * Since a text editor field is scrollable we don't 326 * set a minimumSize. 327 */ 328 public LayoutData getLayoutData() { 329 return new LayoutData(); 330 } 331 332 /** 333 * Processes a key release event that occurred in this cell editor. 334 * 335 * The implementation of this framework method 336 * ignores when the RETURN key is pressed since this is handled in 337 * handleDefaultSelection. 338 * An exception is made for Ctrl+Enter for multi-line texts, since 339 * a default selection event is not sent in this case. 340 * 341 * 342 * @param keyEvent the key event 343 */ 344 protected void keyReleaseOccured(KeyEvent keyEvent) { 345 if (keyEvent.character == '\r') { // Return key 346 // Enter is handled in handleDefaultSelection. 347 // Do not apply the editor value in response to an Enter key event 348 // since this can be received from the IME when the intent is -not- 349 // to apply the value. 350 // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control 351 // 352 // An exception is made for Ctrl+Enter for multi-line texts, since 353 // a default selection event is not sent in this case. 354 if (text != null && !text.isDisposed() 355 && (text.getStyle() & SWT.MULTI) != 0) { 356 if ((keyEvent.stateMask & SWT.CTRL) != 0) { 357 super.keyReleaseOccured(keyEvent); 358 } 359 } 360 return; 361 } 362 super.keyReleaseOccured(keyEvent); 363 } 364 } 365