### Eclipse Workspace Patch 1.0 #P org.eclipse.ui.workbench Index: Eclipse UI/org/eclipse/ui/LegacyHandlerSubmissionExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/LegacyHandlerSubmissionExpression.java,v retrieving revision 1.9 diff -u -r1.9 LegacyHandlerSubmissionExpression.java --- Eclipse UI/org/eclipse/ui/LegacyHandlerSubmissionExpression.java 21 Apr 2006 20:43:38 -0000 1.9 +++ Eclipse UI/org/eclipse/ui/LegacyHandlerSubmissionExpression.java 5 May 2006 03:29:25 -0000 @@ -52,7 +52,7 @@ * true. If this value is null, then any * site may be active. */ - private final IWorkbenchPartSite activeSite; + public final IWorkbenchPartSite activeSite; /** * Constructs a new instance of Index: Eclipse UI/org/eclipse/ui/internal/handlers/HandlerAuthority.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/HandlerAuthority.java,v retrieving revision 1.24 diff -u -r1.24 HandlerAuthority.java --- Eclipse UI/org/eclipse/ui/internal/handlers/HandlerAuthority.java 11 Apr 2006 13:53:54 -0000 1.24 +++ Eclipse UI/org/eclipse/ui/internal/handlers/HandlerAuthority.java 5 May 2006 03:29:26 -0000 @@ -11,7 +11,10 @@ package org.eclipse.ui.internal.handlers; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -22,8 +25,14 @@ import org.eclipse.core.commands.Command; import org.eclipse.core.commands.util.Tracing; import org.eclipse.core.expressions.Expression; +import org.eclipse.core.expressions.IEvaluationContext; import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; import org.eclipse.ui.ISources; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.LegacyHandlerSubmissionExpression; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.internal.misc.Policy; @@ -65,31 +74,31 @@ * This causes the unresolvable handler conflicts to be printed to the * console. */ - private static final boolean DEBUG = Policy.DEBUG_HANDLERS; + public static final boolean DEBUG = Policy.DEBUG_HANDLERS; /** * Whether the performance information should be printed about the * performance of the handler authority. */ - private static final boolean DEBUG_PERFORMANCE = Policy.DEBUG_HANDLERS_PERFORMANCE; + public static final boolean DEBUG_PERFORMANCE = Policy.DEBUG_HANDLERS_PERFORMANCE; /** * Whether the workbench command support should kick into verbose debugging * mode. This causes the resolvable handler conflicts to be printed to the * console. */ - private static final boolean DEBUG_VERBOSE = Policy.DEBUG_HANDLERS + public static final boolean DEBUG_VERBOSE = Policy.DEBUG_HANDLERS && Policy.DEBUG_HANDLERS_VERBOSE; /** * The command identifier to which the verbose output should be restricted. */ - private static final String DEBUG_VERBOSE_COMMAND_ID = Policy.DEBUG_HANDLERS_VERBOSE_COMMAND_ID; + public static final String DEBUG_VERBOSE_COMMAND_ID = Policy.DEBUG_HANDLERS_VERBOSE_COMMAND_ID; /** * The component name to print when displaying tracing information. */ - private static final String TRACING_COMPONENT = "HANDLERS"; //$NON-NLS-1$ + public static final String TRACING_COMPONENT = "HANDLERS"; //$NON-NLS-1$ /** * A bucket sort of the handler activations based on source priority of its @@ -117,6 +126,14 @@ */ private final Map handlerActivationsByCommandId = new HashMap(); + private IHandlerActivation tracingNullHandlerActivation; + + private Exception tracingNullStack; + + private Date sourceChangedStamp; + + private Date resolveConflictsStamp; + /** * Constructs a new instance of HandlerAuthority. * @@ -149,6 +166,9 @@ final SortedSet handlerActivations = (SortedSet) value; if (!handlerActivations.contains(activation)) { handlerActivations.add(activation); + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, resolveConflicts(commandId, handlerActivations)); } @@ -160,11 +180,17 @@ handlerActivations.add(activation); handlerActivationsByCommandId .put(commandId, handlerActivations); + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, resolveConflicts(commandId, handlerActivations)); } } else { handlerActivationsByCommandId.put(commandId, activation); + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, (evaluate(activation) ? activation : null)); } @@ -172,6 +198,13 @@ final int sourcePriority = activation.getSourcePriority(); for (int i = 1; i <= 32; i++) { if ((sourcePriority & (1 << i)) != 0) { + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + Tracing.printTrace(TRACING_COMPONENT, "act-handler " + DEBUG_VERBOSE_COMMAND_ID //$NON-NLS-1$ + + " priority-bit: " + i //$NON-NLS-1$ + + " handler: " + activation); //$NON-NLS-1$ + } Map activationsByExpression = activationsByExpressionBySourcePriority[i]; if (activationsByExpression == null) { activationsByExpression = new HashMap( @@ -208,6 +241,9 @@ handlerActivations.remove(activation); if (handlerActivations.isEmpty()) { handlerActivationsByCommandId.remove(commandId); + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, null); } else if (handlerActivations.size() == 1) { @@ -215,12 +251,19 @@ .iterator().next(); handlerActivationsByCommandId.put(commandId, remainingActivation); + tracingNullHandlerActivation = remainingActivation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); + updateCommand( commandId, (evaluate(remainingActivation) ? remainingActivation : null)); } else { + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, resolveConflicts(commandId, handlerActivations)); } @@ -228,6 +271,9 @@ } else if (value instanceof IHandlerActivation) { if (value == activation) { handlerActivationsByCommandId.remove(commandId); + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, null); } } @@ -286,9 +332,16 @@ final SortedSet activations) { // If we don't have any, then there is no match. if (activations.isEmpty()) { + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + Tracing.printTrace(TRACING_COMPONENT, " rc-empty"); //$NON-NLS-1$ + } return null; } + resolveConflictsStamp = new Date(); + // Cycle over the activations, remembered the current best. final Iterator activationItr = activations.iterator(); IHandlerActivation bestActivation = null; @@ -296,6 +349,12 @@ while (activationItr.hasNext()) { final IHandlerActivation currentActivation = (IHandlerActivation) activationItr .next(); + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + Tracing.printTrace(TRACING_COMPONENT, " rc-con : " + currentActivation); //$NON-NLS-1$ + } + if (!evaluate(currentActivation)) { continue; // only consider potentially active handlers } @@ -304,7 +363,7 @@ if ((DEBUG_VERBOSE) && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID .equals(commandId)))) { - Tracing.printTrace(TRACING_COMPONENT, " resolveConflicts: eval: " + currentActivation); //$NON-NLS-1$ + Tracing.printTrace(TRACING_COMPONENT, " rc-eval: " + currentActivation); //$NON-NLS-1$ } if (bestActivation == null) { bestActivation = currentActivation; @@ -346,6 +405,57 @@ } } + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + if (conflict || bestActivation == null) { + + IWorkbenchWindow wwin = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow(); + if (wwin == null) { + Tracing.printTrace(TRACING_COMPONENT, " wwin is null"); //$NON-NLS-1$ + return null; + } + IWorkbenchPage ap = wwin.getActivePage(); + if (ap == null) { + Tracing.printTrace(TRACING_COMPONENT, " ap is null"); //$NON-NLS-1$ + return null; + } + IEditorPart ae = ap.getActiveEditor(); + if (ae == null) { + Tracing.printTrace(TRACING_COMPONENT, " ae is null"); //$NON-NLS-1$ + } else { + boolean foundOne = false; + for (Iterator it = activations.iterator(); it.hasNext();) { + IHandlerActivation activation = (IHandlerActivation) it + .next(); + Expression ex = activation.getExpression(); + if (ex == null) { + Tracing.printTrace(TRACING_COMPONENT, + " ex is null : " + activation); //$NON-NLS-1$ + } else { + if (!(ex instanceof LegacyHandlerSubmissionExpression)) { + Tracing + .printTrace( + TRACING_COMPONENT, + " ex is not instanceof LegacyHandlerSubmissionExpression: " + activation); //$NON-NLS-1$ + } else { + LegacyHandlerSubmissionExpression lhex = (LegacyHandlerSubmissionExpression) ex; + + if (lhex.activeSite == ae.getSite()) { + printTracing(wwin, ae, activation); + foundOne = true; + } + } + } + } + if (!foundOne) { + Tracing.printTrace(TRACING_COMPONENT, + " Did not find matching handler activation"); //$NON-NLS-1$ + } + } + } + } // Return the current best. if (conflict) { return null; @@ -354,6 +464,50 @@ } /** + * @param wwin + * @param ae + * @param activation + */ + private void printTracing(IWorkbenchWindow wwin, IEditorPart ae, + IHandlerActivation activation) { + + Tracing.printTrace(TRACING_COMPONENT, + " found matching handler activation: " + activation); //$NON-NLS-1$ + ((HandlerActivation) activation).traceStamps(); + long sc = (sourceChangedStamp==null?0L:sourceChangedStamp.getTime()); + long rc = (resolveConflictsStamp==null?0L:resolveConflictsStamp.getTime()); + + Tracing + .printTrace( + TRACING_COMPONENT, + "\tsourceChanged: " + sc //$NON-NLS-1$ + + " resolveConflicts: " + rc); //$NON-NLS-1$ + Tracing + .printTrace( + TRACING_COMPONENT, + " matching handler activation evaluates to: " + evaluate(activation)); //$NON-NLS-1$ + + + IEvaluationContext currentContext = getCurrentState(); + Object cShell = currentContext.getVariable(ISources.ACTIVE_SHELL_NAME); + Object cSite = currentContext.getVariable(ISources.ACTIVE_SITE_NAME); + Tracing.printTrace(TRACING_COMPONENT, " current context shell: " //$NON-NLS-1$ + + cShell + "\n\tshell-match: " //$NON-NLS-1$ + + (cShell == wwin.getShell())); + Tracing.printTrace(TRACING_COMPONENT, " current context site: " //$NON-NLS-1$ + + cSite + "\n\tsite-match: " //$NON-NLS-1$ + + (cSite == ae.getSite())); + + activation.clearResult(); + boolean forcedEval = evaluate(activation); + if (forcedEval) { + System.out.println(); + } + Tracing.printTrace(TRACING_COMPONENT, " forced evaluation: " //$NON-NLS-1$ + + forcedEval); + } + + /** * Carries out the actual source change notification. It assumed that by the * time this method is called, context is up-to-date with the * current state of the application. @@ -367,6 +521,8 @@ if (DEBUG_PERFORMANCE) { startTime = System.currentTimeMillis(); } + + sourceChangedStamp = new Date(); /* * In this first phase, we cycle through all of the activations that @@ -391,6 +547,13 @@ if (activationItr.hasNext()) { IHandlerActivation activation = (IHandlerActivation) activationItr .next(); + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(activation.getCommandId())))) { + Tracing.printTrace(TRACING_COMPONENT, "source-changed: " + DEBUG_VERBOSE_COMMAND_ID //$NON-NLS-1$ + + " priority-bit: " + i //$NON-NLS-1$ + + " handler: " + activation); //$NON-NLS-1$ + } final boolean currentActive = evaluate(activation); activation.clearResult(); final boolean newActive = evaluate(activation); @@ -402,6 +565,13 @@ while (activationItr.hasNext()) { activation = (IHandlerActivation) activationItr .next(); + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(activation.getCommandId())))) { + Tracing.printTrace(TRACING_COMPONENT, "source-changed: " + DEBUG_VERBOSE_COMMAND_ID //$NON-NLS-1$ + + " priority-bit: " + i //$NON-NLS-1$ + + " handler: " + activation); //$NON-NLS-1$ + } // TODO After 3.2, consider making this API. if (activation instanceof EvaluationResultCache) { ((EvaluationResultCache) activation) @@ -412,6 +582,39 @@ changedCommandIds.add(activation .getCommandId()); } + } else { + if(DEBUG_VERBOSE && activations.size()>1) { + IHandlerActivation first = (IHandlerActivation) activations.iterator().next(); + for (Iterator it = activations.iterator(); it + .hasNext();) { + IHandlerActivation handlerActivation = (IHandlerActivation) it + .next(); + if(evaluate(first)!=evaluate(handlerActivation)) { + ((HandlerActivation)handlerActivation).gotcha = new Exception(); + System.out.println("GOTCHA"); //$NON-NLS-1$ + for (Iterator it2 = activations + .iterator(); it2.hasNext();) { + HandlerActivation ha = (HandlerActivation) it2 + .next(); + System.out + .println(evaluate(ha) + " - " + ha); //$NON-NLS-1$ + ha.eval.printStackTrace(System.out); + } + } +// IEvaluationContext context = ((HandlerActivation) handlerActivation).context; +// if (referenceContext != null +// && context != null +// && !Util.equals(referenceContext.getVariable(ISources.ACTIVE_SITE_NAME),context.getVariable(ISources.ACTIVE_SITE_NAME))) { +// Tracing.printTrace(TRACING_COMPONENT, " MISMATCH: found context mismatch for : " + handlerActivation + " expected: " + referenceContext); //$NON-NLS-1$ //$NON-NLS-2$ +// Tracing.printTrace(TRACING_COMPONENT, " MISMATCH: non-matching context has site: " + context.getVariable(ISources.ACTIVE_SITE_NAME)); //$NON-NLS-1$ +// Tracing.printTrace(TRACING_COMPONENT, " MISMATCH: reference activation : " + referenceActivation + " expected: " + referenceContext); //$NON-NLS-1$ //$NON-NLS-2$ +// Tracing.printTrace(TRACING_COMPONENT, " MISMATCH: reference context has site: " + referenceContext.getVariable(ISources.ACTIVE_SITE_NAME)); //$NON-NLS-1$ +// } else if (referenceContext==null && context!=null) { +// referenceContext = context; +// referenceActivation = activation; +// } + } + } } } } @@ -429,13 +632,22 @@ final Object value = handlerActivationsByCommandId.get(commandId); if (value instanceof IHandlerActivation) { final IHandlerActivation activation = (IHandlerActivation) value; + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, (evaluate(activation) ? activation : null)); } else if (value instanceof SortedSet) { final IHandlerActivation activation = resolveConflicts( commandId, (SortedSet) value); + tracingNullHandlerActivation = activation; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, activation); } else { + tracingNullHandlerActivation = null; + tracingNullStack = new Exception(); + tracingNullStack.fillInStackTrace(); updateCommand(commandId, null); } } @@ -465,8 +677,24 @@ final IHandlerActivation activation) { final Command command = commandService.getCommand(commandId); if (activation == null) { + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + Tracing.printTrace(TRACING_COMPONENT, "updateCommand: - " + tracingNullHandlerActivation); //$NON-NLS-1$ + StringWriter wout = new StringWriter(); + tracingNullStack.printStackTrace(new PrintWriter(wout)); + Tracing.printTrace(TRACING_COMPONENT, "updateCommand: n " + wout.toString()); //$NON-NLS-1$ + } command.setHandler(null); } else { + if ((DEBUG_VERBOSE) + && ((DEBUG_VERBOSE_COMMAND_ID == null) || (DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + Tracing.printTrace(TRACING_COMPONENT, "updateCommand: + " + tracingNullHandlerActivation); //$NON-NLS-1$ + StringWriter wout = new StringWriter(); + tracingNullStack.printStackTrace(new PrintWriter(wout)); + Tracing.printTrace(TRACING_COMPONENT, "updateCommand: s " + wout.toString()); //$NON-NLS-1$ + } command.setHandler(activation.getHandler()); } } Index: Eclipse UI/org/eclipse/ui/internal/handlers/HandlerActivation.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/HandlerActivation.java,v retrieving revision 1.8 diff -u -r1.8 HandlerActivation.java --- Eclipse UI/org/eclipse/ui/internal/handlers/HandlerActivation.java 18 Jan 2006 19:37:29 -0000 1.8 +++ Eclipse UI/org/eclipse/ui/internal/handlers/HandlerActivation.java 5 May 2006 03:29:26 -0000 @@ -11,7 +11,12 @@ package org.eclipse.ui.internal.handlers; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Date; + import org.eclipse.core.commands.IHandler; +import org.eclipse.core.commands.util.Tracing; import org.eclipse.core.expressions.Expression; import org.eclipse.core.expressions.IEvaluationContext; import org.eclipse.ui.ISources; @@ -59,6 +64,16 @@ */ private final IHandlerService handlerService; + private Date clearStamp; + + private Date evaluateStamp; + + private Date setStamp; + + private Object cShell; + + private Object cSite; + /** * Constructs a new instance of HandlerActivation. * @@ -123,6 +138,18 @@ final int thisDepth = this.getDepth(); final int thatDepth = activation.getDepth(); difference = thatDepth - thisDepth; + if (difference==0 && this!=activation) { + if ((HandlerAuthority.DEBUG_VERBOSE) + && ((HandlerAuthority.DEBUG_VERBOSE_COMMAND_ID == null) || (HandlerAuthority.DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + StringWriter wout = new StringWriter(); + Exception e = new Exception(); + e.fillInStackTrace(); + e.printStackTrace(new PrintWriter(wout)); + Tracing.printTrace(HandlerAuthority.TRACING_COMPONENT, "compareTo returns 0 at " //$NON-NLS-1$ + + wout.toString()); + } + } return difference; } @@ -161,4 +188,52 @@ return buffer.toString(); } + + /* (non-Javadoc) + * @see org.eclipse.ui.internal.services.EvaluationResultCache#clearResult() + */ + public void clearResult() { + clearStamp = new Date(); + super.clearResult(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.internal.services.EvaluationResultCache#evaluate(org.eclipse.core.expressions.IEvaluationContext) + */ + public boolean evaluate(IEvaluationContext context) { + if (getExpression() != null && evaluationResult == null) { + cShell = context.getVariable(ISources.ACTIVE_SHELL_NAME); + cSite = context.getVariable(ISources.ACTIVE_SITE_NAME); + evaluateStamp = new Date(); + } + return super.evaluate(context); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.internal.services.EvaluationResultCache#setResult(boolean) + */ + public void setResult(boolean result) { + setStamp = new Date(); + super.setResult(result); + } + + /** + * + */ + public void traceStamps() { + if ((HandlerAuthority.DEBUG_VERBOSE) + && ((HandlerAuthority.DEBUG_VERBOSE_COMMAND_ID == null) || (HandlerAuthority.DEBUG_VERBOSE_COMMAND_ID + .equals(commandId)))) { + long eval = (evaluateStamp==null?0L:evaluateStamp.getTime()); + long set = (setStamp==null?0L:setStamp.getTime()); + long clear = (clearStamp==null?0L:clearStamp.getTime()); + Tracing.printTrace(HandlerAuthority.TRACING_COMPONENT, + " activation-eval: " + eval //$NON-NLS-1$ + + " activation-set: " + set //$NON-NLS-1$ + + " activation-clean: " + clear); //$NON-NLS-1$ + Tracing.printTrace(HandlerAuthority.TRACING_COMPONENT, + " last evaluate used shell: " + cShell //$NON-NLS-1$ + + " site: " + cSite); //$NON-NLS-1$ + } + } } Index: Eclipse UI/org/eclipse/ui/internal/SaveablesList.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/SaveablesList.java,v retrieving revision 1.6 diff -u -r1.6 SaveablesList.java --- Eclipse UI/org/eclipse/ui/internal/SaveablesList.java 27 Apr 2006 17:50:01 -0000 1.6 +++ Eclipse UI/org/eclipse/ui/internal/SaveablesList.java 5 May 2006 03:29:26 -0000 @@ -49,6 +49,7 @@ import org.eclipse.ui.ISaveablePart2; import org.eclipse.ui.ISaveablesLifecycleListener; import org.eclipse.ui.ISaveablesSource; +import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPreferenceConstants; import org.eclipse.ui.IWorkbenchWindow; @@ -58,6 +59,7 @@ import org.eclipse.ui.dialogs.ListSelectionDialog; import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; import org.eclipse.ui.internal.util.PrefUtil; +import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.model.WorkbenchPartLabelProvider; /** @@ -184,15 +186,14 @@ break; case SaveablesLifecycleEvent.PRE_CLOSE: Saveable[] models = event.getSaveables(); - Map modelsDecrementing = new HashMap(); - Set modelsClosing = new HashSet(); + CloseInfo closeInfo = new CloseInfo(); for (int i = 0; i < models.length; i++) { - incrementRefCount(modelsDecrementing, models[i]); + incrementRefCount(closeInfo.modelsDecrementing, models[i]); } - fillModelsClosing(modelsClosing, modelsDecrementing); + fillModelsToSave(closeInfo); boolean canceled = promptForSavingIfNecessary(PlatformUI - .getWorkbench().getActiveWorkbenchWindow(), modelsClosing, modelsDecrementing, + .getWorkbench().getActiveWorkbenchWindow(), closeInfo, !event.isForce()); if (canceled) { event.setVeto(true); @@ -289,50 +290,111 @@ * @param editorsToClose * @param save * @param window - * @return the post close info to be passed to postClose + * @return the close info to be passed to postClose, or null if the user canceled */ public Object preCloseParts(List editorsToClose, boolean save, final IWorkbenchWindow window) { // reference count (how many occurrences of a model will go away?) - PostCloseInfo postCloseInfo = new PostCloseInfo(); + CloseInfo closeInfo = new CloseInfo(); + + // Process all parts that implement ISaveablePart or ISaveablePart2. + // Some of these will not want a prompt, or want to prompt on their + // own. We then need to restore the workbench to its previous state, + // for now this is just last active perspective. + // Note that the given parts may come from multiple + // windows, pages and perspectives. + WorkbenchPage currentPage = null; + Perspective currentPageOriginalPerspective = null; for (Iterator it = editorsToClose.iterator(); it.hasNext();) { IWorkbenchPart part = (IWorkbenchPart) it.next(); - postCloseInfo.partsClosing.add(part); - if (part instanceof ISaveablePart) { - ISaveablePart saveablePart = (ISaveablePart) part; - if (save && !saveablePart.isSaveOnCloseNeeded()) { - // pretend for now that this part is not closing - continue; - } - } - if (save && part instanceof ISaveablePart2) { - ISaveablePart2 saveablePart2 = (ISaveablePart2) part; - // TODO show saveablePart2 before prompting, see - // EditorManager.saveAll - int response = SaveableHelper.savePart(saveablePart2, window, - true); - // only include this part in the following logic if it returned - // DEFAULT - if (response != ISaveablePart2.DEFAULT) { - continue; + closeInfo.partsClosing.add(part); + boolean promptForPart = true; + if (save) { + if (part instanceof ISaveablePart) { + ISaveablePart saveablePart = (ISaveablePart) part; + if (!saveablePart.isSaveOnCloseNeeded()) { + promptForPart = false; + } + } + if (promptForPart && part instanceof ISaveablePart2) { + WorkbenchPage page = (WorkbenchPage) part.getSite() + .getPage(); + if (!Util.equals(currentPage, page)) { + if (currentPage != null + && currentPageOriginalPerspective != null) { + if (!currentPageOriginalPerspective + .equals(currentPage.getActivePerspective())) { + currentPage + .setPerspective(currentPageOriginalPerspective + .getDesc()); + } + } + currentPage = page; + currentPageOriginalPerspective = page + .getActivePerspective(); + } + if (part instanceof IViewPart) { + Perspective perspective = page + .getFirstPerspectiveWithView((IViewPart) part); + if (perspective != null) { + page.setPerspective(perspective.getDesc()); + } + } + // show the window containing the page? + IWorkbenchWindow partsWindow = page + .getWorkbenchWindow(); + if (partsWindow != partsWindow.getWorkbench() + .getActiveWorkbenchWindow()) { + Shell shell = partsWindow.getShell(); + if (shell.getMinimized()) { + shell.setMinimized(false); + } + shell.setActive(); + } + page.bringToTop(part); + // try to save the part + int choice = SaveableHelper.savePart((ISaveablePart2) part, + page.getWorkbenchWindow(), true); + if (choice == ISaveablePart2.CANCEL) { + // If the user cancels, don't restore the previous + // workbench state, as that will + // be an unexpected switch from the current state. + return null; + } else if (choice != ISaveablePart2.DEFAULT) { + promptForPart = false; + } } } Saveable[] modelsFromSource = getSaveables(part); for (int i = 0; i < modelsFromSource.length; i++) { - incrementRefCount(postCloseInfo.modelsDecrementing, - modelsFromSource[i]); + incrementRefCount(closeInfo.modelsDecrementing, modelsFromSource[i]); + if(!promptForPart) { + incrementRefCount(closeInfo.modelsDecrementingWithoutPrompt, modelsFromSource[i]); + } + } + if (!promptForPart) { + closeInfo.modelsNotToSave.addAll(Arrays.asList(modelsFromSource)); } } - fillModelsClosing(postCloseInfo.modelsClosing, - postCloseInfo.modelsDecrementing); + + // try to restore the workbench to its previous state + if (currentPage != null && currentPageOriginalPerspective != null) { + if (!currentPageOriginalPerspective.equals(currentPage + .getActivePerspective())) { + currentPage.setPerspective(currentPageOriginalPerspective + .getDesc()); + } + } + + fillModelsToSave(closeInfo); + if (save) { - boolean canceled = promptForSavingIfNecessary(window, - postCloseInfo.modelsClosing, postCloseInfo.modelsDecrementing, true); + boolean canceled = promptForSavingIfNecessary(window, closeInfo, true); if (canceled) { return null; } } - return postCloseInfo; + return closeInfo; } /** @@ -342,11 +404,13 @@ * @return true if the user canceled */ private boolean promptForSavingIfNecessary(final IWorkbenchWindow window, - Set modelsClosing, Map modelsDecrementing, boolean canCancel) { + CloseInfo closeInfo, boolean canCancel) { List modelsToOptionallySave = new ArrayList(); - for (Iterator it = modelsDecrementing.keySet().iterator(); it.hasNext();) { + for (Iterator it = closeInfo.modelsDecrementing.keySet().iterator(); it.hasNext();) { Saveable modelDecrementing = (Saveable) it.next(); - if (modelDecrementing.isDirty() && !modelsClosing.contains(modelDecrementing)) { + if (modelDecrementing.isDirty() + && !closeInfo.modelsToSave.contains(modelDecrementing) + && !closeInfo.modelsNotToSave.contains(modelDecrementing)) { modelsToOptionallySave.add(modelDecrementing); } } @@ -359,7 +423,7 @@ } List modelsToSave = new ArrayList(); - for (Iterator it = modelsClosing.iterator(); it.hasNext();) { + for (Iterator it = closeInfo.modelsToSave.iterator(); it.hasNext();) { Saveable modelClosing = (Saveable) it.next(); if (modelClosing.isDirty()) { modelsToSave.add(modelClosing); @@ -372,12 +436,20 @@ /** * @param modelsClosing * @param modelsDecrementing + * @param partsWithoutPrompt */ - private void fillModelsClosing(Set modelsClosing, Map modelsDecrementing) { - for (Iterator it = modelsDecrementing.keySet().iterator(); it.hasNext();) { + private void fillModelsToSave(CloseInfo closeInfo) { + for (Iterator it = closeInfo.modelsDecrementing.keySet().iterator(); it.hasNext();) { Saveable model = (Saveable) it.next(); - if (modelsDecrementing.get(model).equals(modelRefCounts.get(model))) { - modelsClosing.add(model); + Integer refCount = (Integer) closeInfo.modelsDecrementing.get(model); + if (refCount.equals(modelRefCounts.get(model))) { + // model will be closed. Figure out if we need to prompt + Integer refCountForNotPrompting = (Integer) closeInfo.modelsDecrementingWithoutPrompt.get(model); + if (refCountForNotPrompting==null || refCountForNotPrompting.intValue() < refCount.intValue()) { + closeInfo.modelsToSave.add(model); + } else { + // all ref counts come from parts that decided not to prompt. + } } } } @@ -550,21 +622,30 @@ return false; } - private static class PostCloseInfo { + private static class CloseInfo { + // parts that are being closed private List partsClosing = new ArrayList(); - + + // reference counts for all models that will be closed private Map modelsDecrementing = new HashMap(); - private Set modelsClosing = new HashSet(); + // reference counts for models that will be closed (but no prompting for save for these) + private Map modelsDecrementingWithoutPrompt = new HashMap(); + + // models that are to be saved + private Set modelsToSave = new HashSet(); + + // models that are not to be saved + private Set modelsNotToSave = new HashSet(); } /** - * @param postCloseInfoObject + * @param closeInfoObject */ - public void postClose(Object postCloseInfoObject) { - PostCloseInfo postCloseInfo = (PostCloseInfo) postCloseInfoObject; + public void postClose(Object closeInfoObject) { + CloseInfo closeInfo = (CloseInfo) closeInfoObject; List removed = new ArrayList(); - for (Iterator it = postCloseInfo.partsClosing.iterator(); it.hasNext();) { + for (Iterator it = closeInfo.partsClosing.iterator(); it.hasNext();) { IWorkbenchPart part = (IWorkbenchPart) it.next(); Set saveables = (Set) modelMap.get(part); if (saveables != null) { Index: Eclipse UI/org/eclipse/ui/internal/PartSite.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PartSite.java,v retrieving revision 1.85 diff -u -r1.85 PartSite.java --- Eclipse UI/org/eclipse/ui/internal/PartSite.java 24 Feb 2006 18:36:13 -0000 1.85 +++ Eclipse UI/org/eclipse/ui/internal/PartSite.java 5 May 2006 03:29:25 -0000 @@ -536,6 +536,8 @@ buffer.append(getPluginId()); buffer.append(",registeredName="); //$NON-NLS-1$ buffer.append(getRegisteredName()); + buffer.append(",hashCode="); //$NON-NLS-1$ + buffer.append(hashCode()); buffer.append(')'); return buffer.toString(); } Index: Eclipse UI/org/eclipse/ui/internal/services/EvaluationResultCache.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/EvaluationResultCache.java,v retrieving revision 1.7 diff -u -r1.7 EvaluationResultCache.java --- Eclipse UI/org/eclipse/ui/internal/services/EvaluationResultCache.java 24 Feb 2006 18:36:23 -0000 1.7 +++ Eclipse UI/org/eclipse/ui/internal/services/EvaluationResultCache.java 5 May 2006 03:29:26 -0000 @@ -32,19 +32,25 @@ * The previous computed evaluation result. If no evaluation result is * available, then this value is null. */ - private EvaluationResult evaluationResult = null; + public EvaluationResult evaluationResult = null; /** * The expression to evaluate. This value may be null, in * which case the evaluation result is always true. */ private final Expression expression; + + public IEvaluationContext context; /** * The priority that has been given to this expression. */ private final int sourcePriority; + public Exception eval; + + public Exception gotcha; + /** * Constructs a new instance of EvaluationResultCache. * @@ -60,17 +66,21 @@ .computeSourcePriority(expression); } - public final void clearResult() { + public void clearResult() { evaluationResult = null; + context = null; + eval = null; + gotcha = null; } - public final boolean evaluate(final IEvaluationContext context) { + public boolean evaluate(final IEvaluationContext context) { if (expression == null) { return true; } if (evaluationResult == null) { try { + this.eval = new Exception(); evaluationResult = expression.evaluate(context); } catch (final CoreException e) { /* @@ -100,7 +110,9 @@ * @param result * The cached result to use. */ - public final void setResult(final boolean result) { + public void setResult(final boolean result) { + eval = new Exception(); + gotcha = null; if (result) { evaluationResult = EvaluationResult.TRUE; } else {