/** * Adds an undone NullOperation (i.e. has no effects on executing, undoing or redoing) to the * Eclipse IOperationHistory to be sure that redoing is possible. This is necessary for the UI * Redo possibility being activated. */ protected void simulateUndo() { IUndoableOperation auxOp = new NullOperation(); try { auxOp.addContext(context); eclipseHistory.execute(auxOp, null, null); eclipseHistory.undo(context, null, null); if (!eclipseHistory.canRedo(context)) log.error("Simulating Undo failed"); } catch (ExecutionException e) { log.error("Simulating Undo failed"); } }
@Override public IStatus proceedUndoing( final IUndoableOperation operation, IOperationHistory history, IAdaptable info) { if (!enabled) return Status.OK_STATUS; if (currentActiveEditor == null) { log.info("Undo called on an unknown editor"); return Status.OK_STATUS; } if (log.isDebugEnabled()) log.debug(opInfo(operation)); if (operation.getLabel().equals(TYPING_LABEL)) { updateCurrentLocalAtomicOperation(null); storeCurrentLocalOperation(); SWTUtils.runSafeSWTSync( log, new Runnable() { @Override public void run() { log.debug("undoing operation " + operation); undo(currentActiveEditor); if (undoHistory.canRedo(currentActiveEditor)) simulateUndo(); } }); return Status.CANCEL_STATUS; } return Status.OK_STATUS; }
/* * Determine whether the operation in question is still valid. */ private IStatus proceedWithOperation( final IUndoableOperation operation, final IOperationHistory history, final IAdaptable uiInfo, final int doing) { // return immediately if the operation is not relevant if (!operation.hasContext(context)) { return Status.OK_STATUS; } // if the operation does not support advanced validation, // then we assume it is valid. if (doing == EXECUTING) { if (!(operation instanceof IAdvancedUndoableOperation2)) { return Status.OK_STATUS; } } else { if (!(operation instanceof IAdvancedUndoableOperation)) { return Status.OK_STATUS; } } // The next two methods make a number of UI calls, so we wrap the // whole thing up in a syncExec. final IStatus[] status = new IStatus[1]; Workbench.getInstance() .getDisplay() .syncExec( new Runnable() { public void run() { // Compute the undoable or redoable status status[0] = computeOperationStatus(operation, history, uiInfo, doing); // Report non-OK statuses to the user. In some cases, the user // may choose to proceed, and the returned status will be // different than what is reported. if (!status[0].isOK()) { status[0] = reportAndInterpretStatus(status[0], uiInfo, operation, doing); } } }); // If the operation is still not OK, inform the history that the // operation has changed, since it was previously believed to be valid. // We rely here on the ability of an IAdvancedUndoableOperation to // correctly report canUndo() and canRedo() once the undoable and // redoable status have been computed. if (!status[0].isOK()) { history.operationChanged(operation); } return status[0]; }
@Override public IStatus proceedRedoing( final IUndoableOperation operation, IOperationHistory history, IAdaptable info) { if (!enabled) return Status.OK_STATUS; if (currentActiveEditor == null) { log.info("Redo called on an unknown editor"); return Status.OK_STATUS; } if (log.isDebugEnabled()) log.debug(opInfo(operation)); if (operation.getLabel().equals(TYPING_LABEL) || operation.getLabel().equals(NullOperation.LABEL)) { updateCurrentLocalAtomicOperation(null); storeCurrentLocalOperation(); SWTUtils.runSafeSWTSync( log, new Runnable() { @Override public void run() { log.debug("redoing operation " + operation); redo(currentActiveEditor); /* * For reactivating redo an undo has to be simulated, so * the Eclipse history knows, there is something to * redo. */ if (undoHistory.canRedo(currentActiveEditor)) simulateUndo(); } }); return Status.CANCEL_STATUS; } return Status.OK_STATUS; }
/* * Report a non-OK status to the user */ private IStatus reportAndInterpretStatus( IStatus status, IAdaptable uiInfo, IUndoableOperation operation, int doing) { // Nothing to report if we are running automated tests. We will treat // warnings as if they were authorized by the user. if (AUTOMATED_MODE) { if (status.getSeverity() == IStatus.WARNING) { return Status.OK_STATUS; } return status; } // CANCEL status is assumed to be initiated by the user, so there // is nothing to report. if (status.getSeverity() == IStatus.CANCEL) { return status; } // Other status severities are reported with a message dialog. // First obtain a shell and set up the dialog title. boolean createdShell = false; IStatus reportedStatus = status; Shell shell = getShell(uiInfo); if (shell == null) { createdShell = true; shell = new Shell(); } // Set up the dialog. For non-error statuses, we use a warning dialog // that allows the user to proceed or to cancel out of the operation. if (!(status.getSeverity() == IStatus.ERROR)) { String warning, title; switch (doing) { case UNDOING: warning = WorkbenchMessages.Operations_proceedWithNonOKUndoStatus; if (status.getSeverity() == IStatus.INFO) { title = WorkbenchMessages.Operations_undoInfo; } else { title = WorkbenchMessages.Operations_undoWarning; } break; case REDOING: warning = WorkbenchMessages.Operations_proceedWithNonOKRedoStatus; if (status.getSeverity() == IStatus.INFO) { title = WorkbenchMessages.Operations_redoInfo; } else { title = WorkbenchMessages.Operations_redoWarning; } break; default: // EXECUTING warning = WorkbenchMessages.Operations_proceedWithNonOKExecuteStatus; if (status.getSeverity() == IStatus.INFO) { title = WorkbenchMessages.Operations_executeInfo; } else { title = WorkbenchMessages.Operations_executeWarning; } break; } String message = NLS.bind(warning, new Object[] {status.getMessage(), operation.getLabel()}); String[] buttons = new String[] {IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL}; MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.WARNING, buttons, 0); int dialogAnswer = dialog.open(); // The user has been given the specific status and has chosen // to proceed or to cancel. The user choice determines what // the status should be at this point, OK or CANCEL. if (dialogAnswer == Window.OK) { reportedStatus = Status.OK_STATUS; } else { reportedStatus = Status.CANCEL_STATUS; } } else { String title, stopped; switch (doing) { case UNDOING: title = WorkbenchMessages.Operations_undoProblem; stopped = WorkbenchMessages.Operations_stoppedOnUndoErrorStatus; break; case REDOING: title = WorkbenchMessages.Operations_redoProblem; stopped = WorkbenchMessages.Operations_stoppedOnRedoErrorStatus; break; default: // EXECUTING title = WorkbenchMessages.Operations_executeProblem; stopped = WorkbenchMessages.Operations_stoppedOnExecuteErrorStatus; break; } // It is an error condition. The user has no choice to proceed, so // we only report what has gone on. We use a warning icon instead of // an error icon since there has not yet been a failure. String message = NLS.bind(stopped, status.getMessage(), operation.getLabel()); MessageDialog dialog = new MessageDialog( shell, title, null, message, MessageDialog.WARNING, new String[] {IDialogConstants.OK_LABEL}, 0); // ok dialog.open(); } if (createdShell) { shell.dispose(); } return reportedStatus; }
/** @see IActionDelegate#run(IAction) */ public void run(IAction action) { try { final Element selectedElement = getSelectedElement(); IUndoableOperation operation = new AbstractEMFOperation(editingDomain, "Create Nested Subclass") { @Override protected IStatus doExecute(IProgressMonitor monitor, IAdaptable info) { Property property = null; if (selectedElement instanceof Association) { Association association = (Association) selectedElement; property = UMLUtil.getNavigableEnd(association); } else if (selectedElement instanceof Property) { property = (Property) selectedElement; } if (property == null || !(property.getType() instanceof Class)) { return Status.CANCEL_STATUS; } Class baseClass = (Class) property.getType(); // prompt for new class name String className = null; InputDialog inputDialog = new InputDialog( activePart.getSite().getShell(), "Nested Class", "Enter class name", baseClass.getName(), null); if (inputDialog.open() == Window.OK) { className = inputDialog.getValue(); } if (className == null || className.length() == 0) { return Status.CANCEL_STATUS; } Class newClass = UMLFactory.eINSTANCE.createClass(); property.getClass_().getNestedClassifiers().add(newClass); newClass.setName(className); property.setType(newClass); // create new generalization newClass.createGeneralization(baseClass); // prompt for selection of redefined properties // TODO customize dialog title and prompt SubclassHandler subclassHandler = new SubclassHandler(activePart.getSite().getShell(), newClass); subclassHandler.openSubclassDialog(); // TODO this does not select in CommonNavigator. maybe need a refresh first? if (activePart instanceof ISetSelectionTarget) { ((ISetSelectionTarget) activePart).selectReveal(new StructuredSelection(newClass)); } return Status.OK_STATUS; } }; try { IWorkspaceCommandStack commandStack = (IWorkspaceCommandStack) editingDomain.getCommandStack(); operation.addContext(commandStack.getDefaultUndoContext()); commandStack.getOperationHistory().execute(operation, new NullProgressMonitor(), null); } catch (ExecutionException ee) { Logger.logException(ee); } } catch (Exception e) { throw new RuntimeException(e.getCause()); } }
@Override public IUndoableOperation commit() { final IUndoableOperation updateVisualOperation = super.commit(); if (updateVisualOperation == null) { return null; } // commit changes to model final FXGeometricShapePart host = getHost(); final FXGeometricShape hostContent = host.getContent(); // determine transformation @SuppressWarnings("serial") Provider<Affine> affineProvider = host.getAdapter( AdapterKey.<Provider<? extends Affine>>get( new TypeToken<Provider<? extends Affine>>() {}, FXTransformPolicy.TRANSFORMATION_PROVIDER_ROLE)); AffineTransform tx = JavaFX2Geometry.toAffineTransform(affineProvider.get()); final AffineTransform oldTransform = hostContent.getTransform(); final AffineTransform newTransform = new AffineTransform( tx.getM00(), tx.getM10(), tx.getM01(), tx.getM11(), tx.getTranslateX(), tx.getTranslateY()); // create operation to write the changes to the model final IUndoableOperation updateModelOperation = new AbstractOperation("Update Model") { @Override public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { hostContent.setTransform(newTransform); return Status.OK_STATUS; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } @Override public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { hostContent.setTransform(oldTransform); return Status.OK_STATUS; } }; // compose operations IUndoableOperation compositeOperation = new ForwardUndoCompositeOperation(updateVisualOperation.getLabel()) { { add(updateVisualOperation); add(updateModelOperation); } }; return compositeOperation; }
protected String opInfo(IUndoableOperation operation) { return ("new " + operation.getLabel() + ": " + operation); }