1 /******************************************************************************* 2 * Copyright (c) 2000, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.ui.actions; 15 16 import org.eclipse.jface.action.Action; 17 import org.eclipse.jface.action.IAction; 18 import org.eclipse.jface.util.IPropertyChangeListener; 19 import org.eclipse.jface.util.PropertyChangeEvent; 20 import org.eclipse.swt.SWT; 21 import org.eclipse.swt.dnd.Clipboard; 22 import org.eclipse.swt.dnd.TextTransfer; 23 import org.eclipse.swt.dnd.TransferData; 24 import org.eclipse.swt.events.KeyAdapter; 25 import org.eclipse.swt.events.KeyEvent; 26 import org.eclipse.swt.events.MouseAdapter; 27 import org.eclipse.swt.events.MouseEvent; 28 import org.eclipse.swt.graphics.Point; 29 import org.eclipse.swt.widgets.Event; 30 import org.eclipse.swt.widgets.Listener; 31 import org.eclipse.swt.widgets.Text; 32 import org.eclipse.ui.IActionBars; 33 import org.eclipse.ui.PlatformUI; 34 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; 35 import org.eclipse.ui.internal.ide.IIDEHelpContextIds; 36 37 /** 38 * Handles the redirection of the global Cut, Copy, Paste, and Select All 39 * actions to either the current inline text control or the part's supplied 40 * action handler. 41 * <p> 42 * This class may be instantiated; it is not intended to be subclassed. 43 * </p> 44 * <p> 45 * Example usage: 46 * </p> 47 * 48 * <pre> 49 * textActionHandler = new TextActionHandler(this.getViewSite().getActionBars()); 50 * textActionHandler.addText((Text) textCellEditor1.getControl()); 51 * textActionHandler.addText((Text) textCellEditor2.getControl()); 52 * textActionHandler.setSelectAllAction(selectAllAction); 53 * </pre> 54 * 55 * @noextend This class is not intended to be subclassed by clients. 56 */ 57 public class TextActionHandler { 58 private DeleteActionHandler textDeleteAction = new DeleteActionHandler(); 59 60 private CutActionHandler textCutAction = new CutActionHandler(); 61 62 private CopyActionHandler textCopyAction = new CopyActionHandler(); 63 64 private PasteActionHandler textPasteAction = new PasteActionHandler(); 65 66 private SelectAllActionHandler textSelectAllAction = new SelectAllActionHandler(); 67 68 private IAction deleteAction; 69 70 private IAction cutAction; 71 72 private IAction copyAction; 73 74 private IAction pasteAction; 75 76 private IAction selectAllAction; 77 78 private IPropertyChangeListener deleteActionListener = new PropertyChangeListener( 79 textDeleteAction); 80 81 private IPropertyChangeListener cutActionListener = new PropertyChangeListener( 82 textCutAction); 83 84 private IPropertyChangeListener copyActionListener = new PropertyChangeListener( 85 textCopyAction); 86 87 private IPropertyChangeListener pasteActionListener = new PropertyChangeListener( 88 textPasteAction); 89 90 private IPropertyChangeListener selectAllActionListener = new PropertyChangeListener( 91 textSelectAllAction); 92 93 private Listener textControlListener = new TextControlListener(); 94 95 private Text activeTextControl; 96 97 private IActionBars actionBars; 98 99 private MouseAdapter mouseAdapter = new MouseAdapter() { 100 @Override 101 public void mouseUp(MouseEvent e) { 102 updateActionsEnableState(); 103 } 104 }; 105 106 private KeyAdapter keyAdapter = new KeyAdapter() { 107 @Override 108 public void keyReleased(KeyEvent e) { 109 updateActionsEnableState(); 110 } 111 }; 112 113 private class TextControlListener implements Listener { 114 @Override handleEvent(Event event)115 public void handleEvent(Event event) { 116 switch (event.type) { 117 case SWT.Activate: 118 activeTextControl = (Text) event.widget; 119 updateActionsEnableState(); 120 break; 121 case SWT.Deactivate: 122 activeTextControl = null; 123 updateActionsEnableState(); 124 break; 125 default: 126 break; 127 } 128 } 129 } 130 131 private class PropertyChangeListener implements IPropertyChangeListener { 132 private IAction actionHandler; 133 PropertyChangeListener(IAction actionHandler)134 protected PropertyChangeListener(IAction actionHandler) { 135 super(); 136 this.actionHandler = actionHandler; 137 } 138 139 @Override propertyChange(PropertyChangeEvent event)140 public void propertyChange(PropertyChangeEvent event) { 141 if (activeTextControl != null) { 142 return; 143 } 144 if (event.getProperty().equals(IAction.ENABLED)) { 145 Boolean bool = (Boolean) event.getNewValue(); 146 actionHandler.setEnabled(bool.booleanValue()); 147 } 148 } 149 } 150 151 private class DeleteActionHandler extends Action { DeleteActionHandler()152 protected DeleteActionHandler() { 153 super(IDEWorkbenchMessages.Delete); 154 setId("TextDeleteActionHandler");//$NON-NLS-1$ 155 setEnabled(false); 156 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, 157 IIDEHelpContextIds.TEXT_DELETE_ACTION); 158 } 159 160 @Override runWithEvent(Event event)161 public void runWithEvent(Event event) { 162 if (activeTextControl != null && !activeTextControl.isDisposed()) { 163 Point selection = activeTextControl.getSelection(); 164 if (selection.y == selection.x 165 && selection.x < activeTextControl.getCharCount()) { 166 activeTextControl 167 .setSelection(selection.x, selection.x + 1); 168 } 169 activeTextControl.insert(""); //$NON-NLS-1$ 170 171 updateActionsEnableState(); 172 return; 173 } 174 if (deleteAction != null) { 175 deleteAction.runWithEvent(event); 176 return; 177 } 178 } 179 180 /** 181 * Update state. 182 */ updateEnabledState()183 public void updateEnabledState() { 184 if (activeTextControl != null && !activeTextControl.isDisposed()) { 185 setEnabled(activeTextControl.getEditable() 186 && (activeTextControl.getSelectionCount() > 0 187 || activeTextControl.getCaretPosition() < activeTextControl 188 .getCharCount())); 189 return; 190 } 191 if (deleteAction != null) { 192 setEnabled(deleteAction.isEnabled()); 193 return; 194 } 195 setEnabled(false); 196 } 197 } 198 199 private class CutActionHandler extends Action { 200 protected CutActionHandler() { 201 super(IDEWorkbenchMessages.Cut); 202 setId("TextCutActionHandler");//$NON-NLS-1$ 203 setEnabled(false); 204 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, 205 IIDEHelpContextIds.TEXT_CUT_ACTION); 206 } 207 208 @Override 209 public void runWithEvent(Event event) { 210 if (activeTextControl != null && !activeTextControl.isDisposed()) { 211 activeTextControl.cut(); 212 updateActionsEnableState(); 213 return; 214 } 215 if (cutAction != null) { 216 cutAction.runWithEvent(event); 217 return; 218 } 219 } 220 221 /** 222 * Update state. 223 */ 224 public void updateEnabledState() { 225 if (activeTextControl != null && !activeTextControl.isDisposed()) { 226 setEnabled(activeTextControl.getEditable() && activeTextControl.getSelectionCount() > 0); 227 return; 228 } 229 if (cutAction != null) { 230 setEnabled(cutAction.isEnabled()); 231 return; 232 } 233 setEnabled(false); 234 } 235 } 236 237 private class CopyActionHandler extends Action { CopyActionHandler()238 protected CopyActionHandler() { 239 super(IDEWorkbenchMessages.Copy); 240 setId("TextCopyActionHandler");//$NON-NLS-1$ 241 setEnabled(false); 242 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, 243 IIDEHelpContextIds.TEXT_COPY_ACTION); 244 } 245 246 @Override runWithEvent(Event event)247 public void runWithEvent(Event event) { 248 if (activeTextControl != null && !activeTextControl.isDisposed()) { 249 activeTextControl.copy(); 250 updateActionsEnableState(); 251 return; 252 } 253 if (copyAction != null) { 254 copyAction.runWithEvent(event); 255 return; 256 } 257 } 258 259 /** 260 * Update the state. 261 */ updateEnabledState()262 public void updateEnabledState() { 263 if (activeTextControl != null && !activeTextControl.isDisposed()) { 264 setEnabled(activeTextControl.getSelectionCount() > 0); 265 return; 266 } 267 if (copyAction != null) { 268 setEnabled(copyAction.isEnabled()); 269 return; 270 } 271 setEnabled(false); 272 } 273 } 274 275 private class PasteActionHandler extends Action { PasteActionHandler()276 protected PasteActionHandler() { 277 super(IDEWorkbenchMessages.Paste); 278 setId("TextPasteActionHandler");//$NON-NLS-1$ 279 setEnabled(false); 280 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, 281 IIDEHelpContextIds.TEXT_PASTE_ACTION); 282 } 283 284 @Override runWithEvent(Event event)285 public void runWithEvent(Event event) { 286 if (activeTextControl != null && !activeTextControl.isDisposed()) { 287 activeTextControl.paste(); 288 updateActionsEnableState(); 289 return; 290 } 291 if (pasteAction != null) { 292 pasteAction.runWithEvent(event); 293 return; 294 } 295 } 296 297 /** 298 * Update the state 299 */ updateEnabledState()300 public void updateEnabledState() { 301 if (activeTextControl != null && !activeTextControl.isDisposed()) { 302 boolean canPaste = false; 303 if (activeTextControl.getEditable()) { 304 Clipboard clipboard = new Clipboard(activeTextControl.getDisplay()); 305 for (TransferData transferData : clipboard.getAvailableTypes()) { 306 if (TextTransfer.getInstance().isSupportedType(transferData)) { 307 canPaste = true; 308 break; 309 } 310 } 311 312 clipboard.dispose(); 313 } 314 315 setEnabled(canPaste); 316 return; 317 } 318 if (pasteAction != null) { 319 setEnabled(pasteAction.isEnabled()); 320 return; 321 } 322 setEnabled(false); 323 } 324 } 325 326 private class SelectAllActionHandler extends Action { SelectAllActionHandler()327 protected SelectAllActionHandler() { 328 super(IDEWorkbenchMessages.TextAction_selectAll); 329 setId("TextSelectAllActionHandler");//$NON-NLS-1$ 330 setEnabled(false); 331 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, 332 IIDEHelpContextIds.TEXT_SELECT_ALL_ACTION); 333 } 334 335 @Override runWithEvent(Event event)336 public void runWithEvent(Event event) { 337 if (activeTextControl != null && !activeTextControl.isDisposed()) { 338 activeTextControl.selectAll(); 339 updateActionsEnableState(); 340 return; 341 } 342 if (selectAllAction != null) { 343 selectAllAction.runWithEvent(event); 344 return; 345 } 346 } 347 348 /** 349 * Update the state. 350 */ updateEnabledState()351 public void updateEnabledState() { 352 if (activeTextControl != null && !activeTextControl.isDisposed()) { 353 setEnabled(activeTextControl.getCharCount() > 0); 354 return; 355 } 356 if (selectAllAction != null) { 357 setEnabled(selectAllAction.isEnabled()); 358 return; 359 } 360 setEnabled(false); 361 } 362 } 363 364 /** 365 * Creates a <code>Text</code> control action handler 366 * for the global Cut, Copy, Paste, Delete, and Select All 367 * of the action bar. 368 * 369 * @param actionBar the action bar to register global 370 * action handlers for Cut, Copy, Paste, Delete, 371 * and Select All 372 */ TextActionHandler(IActionBars actionBar)373 public TextActionHandler(IActionBars actionBar) { 374 super(); 375 actionBars = actionBar; 376 updateActionBars(); 377 } 378 379 /** 380 * Updates the actions bars. 381 * 382 * @since 3.6 383 */ updateActionBars()384 public void updateActionBars() { 385 actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), 386 textCutAction); 387 actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), 388 textCopyAction); 389 actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), 390 textPasteAction); 391 actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), 392 textSelectAllAction); 393 actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(), 394 textDeleteAction); 395 } 396 397 398 /** 399 * Add a <code>Text</code> control to the handler 400 * so that the Cut, Copy, Paste, Delete, and Select All 401 * actions are redirected to it when active. 402 * 403 * @param textControl the inline <code>Text</code> control 404 */ addText(Text textControl)405 public void addText(Text textControl) { 406 if (textControl == null) { 407 return; 408 } 409 410 textControl.addListener(SWT.Activate, textControlListener); 411 textControl.addListener(SWT.Deactivate, textControlListener); 412 413 // We really want a selection listener but it is not supported so we 414 // use a key listener and a mouse listener to know when selection changes 415 // may have occured 416 textControl.addKeyListener(keyAdapter); 417 textControl.addMouseListener(mouseAdapter); 418 419 if (textControl.isFocusControl()) { 420 activeTextControl = textControl; 421 updateActionsEnableState(); 422 } 423 } 424 425 /** 426 * Dispose of this action handler 427 */ dispose()428 public void dispose() { 429 setCutAction(null); 430 setCopyAction(null); 431 setPasteAction(null); 432 setSelectAllAction(null); 433 setDeleteAction(null); 434 } 435 436 /** 437 * Removes a <code>Text</code> control from the handler 438 * so that the Cut, Copy, Paste, Delete, and Select All 439 * actions are no longer redirected to it when active. 440 * 441 * @param textControl the inline <code>Text</code> control 442 */ removeText(Text textControl)443 public void removeText(Text textControl) { 444 if (textControl == null) { 445 return; 446 } 447 448 textControl.removeListener(SWT.Activate, textControlListener); 449 textControl.removeListener(SWT.Deactivate, textControlListener); 450 451 textControl.removeMouseListener(mouseAdapter); 452 textControl.removeKeyListener(keyAdapter); 453 454 activeTextControl = null; 455 updateActionsEnableState(); 456 } 457 458 /** 459 * Set the default <code>IAction</code> handler for the Copy 460 * action. This <code>IAction</code> is run only if no active 461 * inline text control. 462 * 463 * @param action the <code>IAction</code> to run for the 464 * Copy action, or <code>null</code> if not interested. 465 */ setCopyAction(IAction action)466 public void setCopyAction(IAction action) { 467 if (copyAction == action) { 468 return; 469 } 470 471 if (copyAction != null) { 472 copyAction.removePropertyChangeListener(copyActionListener); 473 } 474 475 copyAction = action; 476 477 if (copyAction != null) { 478 copyAction.addPropertyChangeListener(copyActionListener); 479 } 480 481 textCopyAction.updateEnabledState(); 482 } 483 484 /** 485 * Set the default <code>IAction</code> handler for the Cut 486 * action. This <code>IAction</code> is run only if no active 487 * inline text control. 488 * 489 * @param action the <code>IAction</code> to run for the 490 * Cut action, or <code>null</code> if not interested. 491 */ setCutAction(IAction action)492 public void setCutAction(IAction action) { 493 if (cutAction == action) { 494 return; 495 } 496 497 if (cutAction != null) { 498 cutAction.removePropertyChangeListener(cutActionListener); 499 } 500 501 cutAction = action; 502 503 if (cutAction != null) { 504 cutAction.addPropertyChangeListener(cutActionListener); 505 } 506 507 textCutAction.updateEnabledState(); 508 } 509 510 /** 511 * Set the default <code>IAction</code> handler for the Paste 512 * action. This <code>IAction</code> is run only if no active 513 * inline text control. 514 * 515 * @param action the <code>IAction</code> to run for the 516 * Paste action, or <code>null</code> if not interested. 517 */ setPasteAction(IAction action)518 public void setPasteAction(IAction action) { 519 if (pasteAction == action) { 520 return; 521 } 522 523 if (pasteAction != null) { 524 pasteAction.removePropertyChangeListener(pasteActionListener); 525 } 526 527 pasteAction = action; 528 529 if (pasteAction != null) { 530 pasteAction.addPropertyChangeListener(pasteActionListener); 531 } 532 533 textPasteAction.updateEnabledState(); 534 } 535 536 /** 537 * Set the default <code>IAction</code> handler for the Select All 538 * action. This <code>IAction</code> is run only if no active 539 * inline text control. 540 * 541 * @param action the <code>IAction</code> to run for the 542 * Select All action, or <code>null</code> if not interested. 543 */ setSelectAllAction(IAction action)544 public void setSelectAllAction(IAction action) { 545 if (selectAllAction == action) { 546 return; 547 } 548 549 if (selectAllAction != null) { 550 selectAllAction 551 .removePropertyChangeListener(selectAllActionListener); 552 } 553 554 selectAllAction = action; 555 556 if (selectAllAction != null) { 557 selectAllAction.addPropertyChangeListener(selectAllActionListener); 558 } 559 560 textSelectAllAction.updateEnabledState(); 561 } 562 563 /** 564 * Set the default <code>IAction</code> handler for the Delete 565 * action. This <code>IAction</code> is run only if no active 566 * inline text control. 567 * 568 * @param action the <code>IAction</code> to run for the 569 * Delete action, or <code>null</code> if not interested. 570 */ setDeleteAction(IAction action)571 public void setDeleteAction(IAction action) { 572 if (deleteAction == action) { 573 return; 574 } 575 576 if (deleteAction != null) { 577 deleteAction.removePropertyChangeListener(deleteActionListener); 578 } 579 580 deleteAction = action; 581 582 if (deleteAction != null) { 583 deleteAction.addPropertyChangeListener(deleteActionListener); 584 } 585 586 textDeleteAction.updateEnabledState(); 587 } 588 589 /** 590 * Update the enable state of the Cut, Copy, 591 * Paste, Delete, and Select All action handlers 592 */ updateActionsEnableState()593 private void updateActionsEnableState() { 594 textCutAction.updateEnabledState(); 595 textCopyAction.updateEnabledState(); 596 textPasteAction.updateEnabledState(); 597 textSelectAllAction.updateEnabledState(); 598 textDeleteAction.updateEnabledState(); 599 } 600 } 601