1 11 12 package org.eclipse.ui.internal; 13 14 import java.util.ArrayList ; 15 import java.util.Arrays ; 16 import java.util.HashMap ; 17 import java.util.HashSet ; 18 import java.util.Iterator ; 19 import java.util.List ; 20 import java.util.Map ; 21 import java.util.Set ; 22 23 import org.eclipse.core.runtime.Assert; 24 import org.eclipse.core.runtime.AssertionFailedException; 25 import org.eclipse.core.runtime.IProgressMonitor; 26 import org.eclipse.core.runtime.IStatus; 27 import org.eclipse.core.runtime.ListenerList; 28 import org.eclipse.core.runtime.SubProgressMonitor; 29 import org.eclipse.jface.dialogs.IDialogConstants; 30 import org.eclipse.jface.dialogs.MessageDialog; 31 import org.eclipse.jface.dialogs.MessageDialogWithToggle; 32 import org.eclipse.jface.operation.IRunnableContext; 33 import org.eclipse.jface.operation.IRunnableWithProgress; 34 import org.eclipse.jface.preference.IPreferenceStore; 35 import org.eclipse.jface.viewers.ArrayContentProvider; 36 import org.eclipse.jface.viewers.ILabelProvider; 37 import org.eclipse.jface.viewers.IStructuredContentProvider; 38 import org.eclipse.jface.window.IShellProvider; 39 import org.eclipse.osgi.util.NLS; 40 import org.eclipse.swt.SWT; 41 import org.eclipse.swt.events.SelectionAdapter; 42 import org.eclipse.swt.events.SelectionEvent; 43 import org.eclipse.swt.layout.GridData; 44 import org.eclipse.swt.layout.GridLayout; 45 import org.eclipse.swt.widgets.Button; 46 import org.eclipse.swt.widgets.Composite; 47 import org.eclipse.swt.widgets.Control; 48 import org.eclipse.swt.widgets.Label; 49 import org.eclipse.swt.widgets.Shell; 50 import org.eclipse.ui.ISaveablePart; 51 import org.eclipse.ui.ISaveablePart2; 52 import org.eclipse.ui.ISaveablesLifecycleListener; 53 import org.eclipse.ui.ISaveablesSource; 54 import org.eclipse.ui.IWorkbenchPart; 55 import org.eclipse.ui.IWorkbenchPreferenceConstants; 56 import org.eclipse.ui.IWorkbenchWindow; 57 import org.eclipse.ui.PlatformUI; 58 import org.eclipse.ui.Saveable; 59 import org.eclipse.ui.SaveablesLifecycleEvent; 60 import org.eclipse.ui.dialogs.ListSelectionDialog; 61 import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; 62 import org.eclipse.ui.internal.misc.StatusUtil; 63 import org.eclipse.ui.internal.util.PrefUtil; 64 import org.eclipse.ui.model.WorkbenchPartLabelProvider; 65 66 74 public class SaveablesList implements ISaveablesLifecycleListener { 75 76 private ListenerList listeners = new ListenerList(); 77 78 private Map modelMap = new HashMap (); 80 81 private Map modelRefCounts = new HashMap (); 83 84 private Set nonPartSources = new HashSet (); 85 86 91 public Saveable[] getOpenModels() { 92 Set allDistinctModels = new HashSet (); 93 Iterator saveables = modelMap.values().iterator(); 94 while (saveables.hasNext()) 95 allDistinctModels.addAll((Set )saveables.next()); 96 97 return (Saveable[]) allDistinctModels.toArray( 98 new Saveable[allDistinctModels.size()]); 99 } 100 101 private boolean addModel(Object source, Saveable model) { 103 boolean result = false; 104 Set modelsForSource = (Set ) modelMap.get(source); 105 if (modelsForSource == null) { 106 modelsForSource = new HashSet (); 107 modelMap.put(source, modelsForSource); 108 } 109 if (modelsForSource.add(model)) { 110 result = incrementRefCount(modelRefCounts, model); 111 } else { 112 logWarning( 113 "Ignored attempt to add saveable that was already registered", source, model); } 115 return result; 116 } 117 118 125 private boolean incrementRefCount(Map referenceMap, Object key) { 126 boolean result = false; 127 Integer refCount = (Integer ) referenceMap.get(key); 128 if (refCount == null) { 129 result = true; 130 refCount = new Integer (0); 131 } 132 referenceMap.put(key, new Integer (refCount.intValue() + 1)); 133 return result; 134 } 135 136 143 private boolean decrementRefCount(Map referenceMap, Object key) { 144 boolean result = false; 145 Integer refCount = (Integer ) referenceMap.get(key); 146 Assert.isTrue(refCount != null); 147 if (refCount.intValue() == 1) { 148 referenceMap.remove(key); 149 result = true; 150 } else { 151 referenceMap.put(key, new Integer (refCount.intValue() - 1)); 152 } 153 return result; 154 } 155 156 private boolean removeModel(Object source, Saveable model) { 158 boolean result = false; 159 Set modelsForSource = (Set ) modelMap.get(source); 160 if (modelsForSource == null) { 161 logWarning( 162 "Ignored attempt to remove a saveable when no saveables were known", source, model); } else { 164 if (modelsForSource.remove(model)) { 165 result = decrementRefCount(modelRefCounts, model); 166 if (modelsForSource.isEmpty()) { 167 modelMap.remove(source); 168 } 169 } else { 170 logWarning( 171 "Ignored attempt to remove a saveable that was not registered", source, model); } 173 } 174 return result; 175 } 176 177 private void logWarning(String message, Object source, Saveable model) { 178 AssertionFailedException assertionFailedException = new AssertionFailedException("unknown saveable: " + model + " from part: " + source); assertionFailedException.fillInStackTrace(); 183 WorkbenchPlugin.log(StatusUtil.newStatus(IStatus.WARNING, message, 184 assertionFailedException)); 185 } 186 187 206 public void handleLifecycleEvent(SaveablesLifecycleEvent event) { 207 if (!(event.getSource() instanceof IWorkbenchPart)) { 208 updateNonPartSource((ISaveablesSource) event.getSource()); 211 return; 212 } 213 Saveable[] modelArray = event.getSaveables(); 214 switch (event.getEventType()) { 215 case SaveablesLifecycleEvent.POST_OPEN: 216 addModels(event.getSource(), modelArray); 217 break; 218 case SaveablesLifecycleEvent.PRE_CLOSE: 219 Saveable[] models = event.getSaveables(); 220 Map modelsDecrementing = new HashMap (); 221 Set modelsClosing = new HashSet (); 222 for (int i = 0; i < models.length; i++) { 223 incrementRefCount(modelsDecrementing, models[i]); 224 } 225 226 fillModelsClosing(modelsClosing, modelsDecrementing); 227 boolean canceled = promptForSavingIfNecessary(PlatformUI 228 .getWorkbench().getActiveWorkbenchWindow(), modelsClosing, modelsDecrementing, 229 !event.isForce()); 230 if (canceled) { 231 event.setVeto(true); 232 } 233 break; 234 case SaveablesLifecycleEvent.POST_CLOSE: 235 removeModels(event.getSource(), modelArray); 236 break; 237 case SaveablesLifecycleEvent.DIRTY_CHANGED: 238 fireModelLifecycleEvent(new SaveablesLifecycleEvent(this, event 239 .getEventType(), event.getSaveables(), false)); 240 break; 241 } 242 } 243 244 248 private void updateNonPartSource(ISaveablesSource source) { 249 Saveable[] saveables = source.getSaveables(); 250 if (saveables.length == 0) { 251 nonPartSources.remove(source); 252 } else { 253 nonPartSources.add(source); 254 } 255 } 256 257 261 private void removeModels(Object source, Saveable[] modelArray) { 262 List removed = new ArrayList (); 263 for (int i = 0; i < modelArray.length; i++) { 264 Saveable model = modelArray[i]; 265 if (removeModel(source, model)) { 266 removed.add(model); 267 } 268 } 269 if (removed.size() > 0) { 270 fireModelLifecycleEvent(new SaveablesLifecycleEvent(this, 271 SaveablesLifecycleEvent.POST_OPEN, (Saveable[]) removed 272 .toArray(new Saveable[removed.size()]), false)); 273 } 274 } 275 276 280 private void addModels(Object source, Saveable[] modelArray) { 281 List added = new ArrayList (); 282 for (int i = 0; i < modelArray.length; i++) { 283 Saveable model = modelArray[i]; 284 if (addModel(source, model)) { 285 added.add(model); 286 } 287 } 288 if (added.size() > 0) { 289 fireModelLifecycleEvent(new SaveablesLifecycleEvent(this, 290 SaveablesLifecycleEvent.POST_OPEN, (Saveable[]) added 291 .toArray(new Saveable[added.size()]), false)); 292 } 293 } 294 295 298 private void fireModelLifecycleEvent(SaveablesLifecycleEvent event) { 299 Object [] listenerArray = listeners.getListeners(); 300 for (int i = 0; i < listenerArray.length; i++) { 301 ((ISaveablesLifecycleListener) listenerArray[i]) 302 .handleLifecycleEvent(event); 303 } 304 } 305 306 320 public void addModelLifecycleListener(ISaveablesLifecycleListener listener) { 321 listeners.add(listener); 322 } 323 324 330 public void removeModelLifecycleListener(ISaveablesLifecycleListener listener) { 331 listeners.remove(listener); 332 } 333 334 340 public Object preCloseParts(List partsToClose, boolean save, 341 final IWorkbenchWindow window) { 342 PostCloseInfo postCloseInfo = new PostCloseInfo(); 344 for (Iterator it = partsToClose.iterator(); it.hasNext();) { 345 IWorkbenchPart part = (IWorkbenchPart) it.next(); 346 postCloseInfo.partsClosing.add(part); 347 if (part instanceof ISaveablePart) { 348 ISaveablePart saveablePart = (ISaveablePart) part; 349 if (save && !saveablePart.isSaveOnCloseNeeded()) { 350 continue; 352 } 353 } 354 if (save && part instanceof ISaveablePart2) { 355 ISaveablePart2 saveablePart2 = (ISaveablePart2) part; 356 int response = SaveableHelper.savePart(saveablePart2, window, 359 true); 360 if (response == ISaveablePart2.CANCEL) { 361 return null; 363 } else if (response != ISaveablePart2.DEFAULT) { 364 continue; 367 } 368 } 369 Saveable[] modelsFromSource = getSaveables(part); 370 for (int i = 0; i < modelsFromSource.length; i++) { 371 incrementRefCount(postCloseInfo.modelsDecrementing, 372 modelsFromSource[i]); 373 } 374 } 375 fillModelsClosing(postCloseInfo.modelsClosing, 376 postCloseInfo.modelsDecrementing); 377 if (save) { 378 boolean canceled = promptForSavingIfNecessary(window, 379 postCloseInfo.modelsClosing, postCloseInfo.modelsDecrementing, true); 380 if (canceled) { 381 return null; 382 } 383 } 384 return postCloseInfo; 385 } 386 387 393 private boolean promptForSavingIfNecessary(final IWorkbenchWindow window, 394 Set modelsClosing, Map modelsDecrementing, boolean canCancel) { 395 List modelsToOptionallySave = new ArrayList (); 396 for (Iterator it = modelsDecrementing.keySet().iterator(); it.hasNext();) { 397 Saveable modelDecrementing = (Saveable) it.next(); 398 if (modelDecrementing.isDirty() && !modelsClosing.contains(modelDecrementing)) { 399 modelsToOptionallySave.add(modelDecrementing); 400 } 401 } 402 403 boolean shouldCancel = modelsToOptionallySave.isEmpty() ? false : promptForSaving(modelsToOptionallySave, 404 window, window, canCancel, true); 405 406 if (shouldCancel) { 407 return true; 408 } 409 410 List modelsToSave = new ArrayList (); 411 for (Iterator it = modelsClosing.iterator(); it.hasNext();) { 412 Saveable modelClosing = (Saveable) it.next(); 413 if (modelClosing.isDirty()) { 414 modelsToSave.add(modelClosing); 415 } 416 } 417 return modelsToSave.isEmpty() ? false : promptForSaving(modelsToSave, 418 window, window, canCancel, false); 419 } 420 421 425 private void fillModelsClosing(Set modelsClosing, Map modelsDecrementing) { 426 for (Iterator it = modelsDecrementing.keySet().iterator(); it.hasNext();) { 427 Saveable model = (Saveable) it.next(); 428 if (modelsDecrementing.get(model).equals(modelRefCounts.get(model))) { 429 modelsClosing.add(model); 430 } 431 } 432 } 433 434 446 public boolean promptForSaving(List modelsToSave, 447 final IShellProvider shellProvider, IRunnableContext runnableContext, final boolean canCancel, boolean stillOpenElsewhere) { 448 if (modelsToSave.size() > 0) { 450 boolean canceled = SaveableHelper.waitForBackgroundSaveJobs(modelsToSave); 451 if (canceled) { 452 return true; 453 } 454 455 IPreferenceStore apiPreferenceStore = PrefUtil.getAPIPreferenceStore(); 456 boolean dontPrompt = stillOpenElsewhere && !apiPreferenceStore.getBoolean(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN); 457 458 if (dontPrompt) { 459 modelsToSave.clear(); 460 return false; 461 } else if (modelsToSave.size() == 1) { 462 Saveable model = (Saveable) modelsToSave.get(0); 463 String [] buttons; 465 if(canCancel) { 466 buttons = new String [] { IDialogConstants.YES_LABEL, 467 IDialogConstants.NO_LABEL, 468 IDialogConstants.CANCEL_LABEL }; 469 } else { 470 buttons = new String [] { IDialogConstants.YES_LABEL, 471 IDialogConstants.NO_LABEL}; 472 } 473 474 int choice = ISaveablePart2.NO; 476 477 MessageDialog dialog; 478 if (stillOpenElsewhere) { 479 String message = NLS 480 .bind( 481 WorkbenchMessages.EditorManager_saveChangesOptionallyQuestion, 482 model.getName()); 483 MessageDialogWithToggle dialogWithToggle = new MessageDialogWithToggle(shellProvider.getShell(), 484 WorkbenchMessages.Save_Resource, null, message, 485 MessageDialog.QUESTION, buttons, 0, WorkbenchMessages.EditorManager_closeWithoutPromptingOption, false) { 486 protected int getShellStyle() { 487 return (canCancel ? SWT.CLOSE : SWT.NONE) 488 | SWT.TITLE | SWT.BORDER 489 | SWT.APPLICATION_MODAL 490 | getDefaultOrientation(); 491 } 492 }; 493 dialog = dialogWithToggle; 494 } else { 495 String message = NLS 496 .bind( 497 WorkbenchMessages.EditorManager_saveChangesQuestion, 498 model.getName()); 499 dialog = new MessageDialog(shellProvider.getShell(), 500 WorkbenchMessages.Save_Resource, null, message, 501 MessageDialog.QUESTION, buttons, 0) { 502 protected int getShellStyle() { 503 return (canCancel ? SWT.CLOSE : SWT.NONE) 504 | SWT.TITLE | SWT.BORDER 505 | SWT.APPLICATION_MODAL 506 | getDefaultOrientation(); 507 } 508 }; 509 } 510 511 choice = SaveableHelper.testGetAutomatedResponse(); 512 if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) { 513 choice = dialog.open(); 514 515 if(stillOpenElsewhere) { 516 switch (choice) { 518 case IDialogConstants.YES_ID: 519 choice = ISaveablePart2.YES; 520 break; 521 case IDialogConstants.NO_ID: 522 choice = ISaveablePart2.NO; 523 break; 524 case IDialogConstants.CANCEL_ID: 525 choice = ISaveablePart2.CANCEL; 526 break; 527 default: 528 break; 529 } 530 MessageDialogWithToggle dialogWithToggle = (MessageDialogWithToggle) dialog; 531 if (choice != ISaveablePart2.CANCEL && dialogWithToggle.getToggleState()) { 532 apiPreferenceStore.setValue(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN, false); 533 } 534 } 535 } 536 537 switch (choice) { 541 case ISaveablePart2.YES: break; 543 case ISaveablePart2.NO: modelsToSave.clear(); 545 break; 546 default: 547 case ISaveablePart2.CANCEL: return true; 549 } 550 } else { 551 MyListSelectionDialog dlg = new MyListSelectionDialog( 552 shellProvider.getShell(), 553 modelsToSave, 554 new ArrayContentProvider(), 555 new WorkbenchPartLabelProvider(), 556 stillOpenElsewhere ? WorkbenchMessages.EditorManager_saveResourcesOptionallyMessage 557 : WorkbenchMessages.EditorManager_saveResourcesMessage, 558 canCancel, stillOpenElsewhere); 559 dlg.setInitialSelections(modelsToSave.toArray()); 560 dlg.setTitle(EditorManager.SAVE_RESOURCES_TITLE); 561 562 if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) { 564 int result = dlg.open(); 565 if (result == IDialogConstants.CANCEL_ID) 567 return true; 568 569 if (dlg.getDontPromptSelection()) { 570 apiPreferenceStore.setValue(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN, false); 571 } 572 573 modelsToSave = Arrays.asList(dlg.getResult()); 574 } 575 } 576 } 577 return saveModels(modelsToSave, shellProvider, runnableContext); 579 } 580 581 591 public boolean saveModels(final List finalModels, final IShellProvider shellProvider, IRunnableContext runnableContext) { 592 IRunnableWithProgress progressOp = new IRunnableWithProgress() { 593 public void run(IProgressMonitor monitor) { 594 IProgressMonitor monitorWrap = new EventLoopProgressMonitor( 595 monitor); 596 monitorWrap.beginTask("", finalModels.size()); for (Iterator i = finalModels.iterator(); i.hasNext();) { 598 Saveable model = (Saveable) i.next(); 599 if (!model.isDirty()) { 602 monitor.worked(1); 603 continue; 604 } 605 SaveableHelper.doSaveModel(model, new SubProgressMonitor(monitorWrap, 1), shellProvider, true); 606 if (monitorWrap.isCanceled()) 607 break; 608 } 609 monitorWrap.done(); 610 } 611 }; 612 613 return !SaveableHelper.runProgressMonitorOperation( 615 WorkbenchMessages.Save_All, progressOp, runnableContext, 616 shellProvider); 617 } 618 619 private static class PostCloseInfo { 620 private List partsClosing = new ArrayList (); 621 622 private Map modelsDecrementing = new HashMap (); 623 624 private Set modelsClosing = new HashSet (); 625 } 626 627 630 public void postClose(Object postCloseInfoObject) { 631 PostCloseInfo postCloseInfo = (PostCloseInfo) postCloseInfoObject; 632 List removed = new ArrayList (); 633 for (Iterator it = postCloseInfo.partsClosing.iterator(); it.hasNext();) { 634 IWorkbenchPart part = (IWorkbenchPart) it.next(); 635 Set saveables = (Set ) modelMap.get(part); 636 if (saveables != null) { 637 saveables = new HashSet (saveables); 640 for (Iterator it2 = saveables.iterator(); it2.hasNext();) { 641 Saveable saveable = (Saveable) it2.next(); 642 if (removeModel(part, saveable)) { 643 removed.add(saveable); 644 } 645 } 646 } 647 } 648 if (removed.size() > 0) { 649 fireModelLifecycleEvent(new SaveablesLifecycleEvent(this, 650 SaveablesLifecycleEvent.POST_CLOSE, (Saveable[]) removed 651 .toArray(new Saveable[removed.size()]), false)); 652 } 653 } 654 655 664 private Saveable[] getSaveables(IWorkbenchPart part) { 665 if (part instanceof ISaveablesSource) { 666 ISaveablesSource source = (ISaveablesSource) part; 667 return source.getSaveables(); 668 } else if (part instanceof ISaveablePart) { 669 return new Saveable[] { new DefaultSaveable(part) }; 670 } else { 671 return new Saveable[0]; 672 } 673 } 674 675 678 public void postOpen(IWorkbenchPart part) { 679 addModels(part, getSaveables(part)); 680 } 681 682 685 public void dirtyChanged(IWorkbenchPart part) { 686 Saveable[] saveables = getSaveables(part); 687 if (saveables.length > 0) { 688 fireModelLifecycleEvent(new SaveablesLifecycleEvent(this, 689 SaveablesLifecycleEvent.DIRTY_CHANGED, saveables, false)); 690 } 691 } 692 693 699 public Object [] testGetSourcesForModel(Saveable model) { 700 List result = new ArrayList (); 701 for (Iterator it = modelMap.entrySet().iterator(); it.hasNext();) { 702 Map.Entry entry = (Map.Entry ) it.next(); 703 Set values = (Set ) entry.getValue(); 704 if (values.contains(model)) { 705 result.add(entry.getKey()); 706 } 707 } 708 return result.toArray(); 709 } 710 711 private static final class MyListSelectionDialog extends 712 ListSelectionDialog { 713 private final boolean canCancel; 714 private Button checkbox; 715 private boolean dontPromptSelection; 716 private boolean stillOpenElsewhere; 717 718 private MyListSelectionDialog(Shell shell, Object input, 719 IStructuredContentProvider contentprovider, 720 ILabelProvider labelProvider, String message, boolean canCancel, boolean stillOpenElsewhere) { 721 super(shell, input, contentprovider, labelProvider, message); 722 this.canCancel = canCancel; 723 this.stillOpenElsewhere = stillOpenElsewhere; 724 if (!canCancel) { 725 int shellStyle = getShellStyle(); 726 shellStyle &= ~SWT.CLOSE; 727 setShellStyle(shellStyle); 728 } 729 } 730 731 734 public boolean getDontPromptSelection() { 735 return dontPromptSelection; 736 } 737 738 protected void createButtonsForButtonBar(Composite parent) { 739 createButton(parent, IDialogConstants.OK_ID, 740 IDialogConstants.OK_LABEL, true); 741 if (canCancel) { 742 createButton(parent, IDialogConstants.CANCEL_ID, 743 IDialogConstants.CANCEL_LABEL, false); 744 } 745 } 746 747 protected Control createDialogArea(Composite parent) { 748 Composite dialogAreaComposite = (Composite) super.createDialogArea(parent); 749 750 if (stillOpenElsewhere) { 751 Composite checkboxComposite = new Composite(dialogAreaComposite, SWT.NONE); 752 checkboxComposite.setLayout(new GridLayout(2, false)); 753 754 checkbox = new Button(checkboxComposite, SWT.CHECK); 755 checkbox.addSelectionListener(new SelectionAdapter() { 756 public void widgetSelected(SelectionEvent e) { 757 dontPromptSelection = checkbox.getSelection(); 758 } 759 }); 760 GridData gd = new GridData(); 761 gd.horizontalAlignment = SWT.BEGINNING; 762 checkbox.setLayoutData(gd); 763 764 Label label = new Label(checkboxComposite, SWT.NONE); 765 label.setText(WorkbenchMessages.EditorManager_closeWithoutPromptingOption); 766 gd = new GridData(); 767 gd.grabExcessHorizontalSpace = true; 768 gd.horizontalAlignment = SWT.BEGINNING; 769 } 770 771 return dialogAreaComposite; 772 } 773 } 774 775 779 public ISaveablesSource[] getNonPartSources() { 780 return (ISaveablesSource[]) nonPartSources 781 .toArray(new ISaveablesSource[nonPartSources.size()]); 782 } 783 784 787 public IWorkbenchPart[] getPartsForSaveable(Saveable model) { 788 List result = new ArrayList (); 789 for (Iterator it = modelMap.entrySet().iterator(); it.hasNext();) { 790 Map.Entry entry = (Map.Entry ) it.next(); 791 Set values = (Set ) entry.getValue(); 792 if (values.contains(model) && entry.getKey() instanceof IWorkbenchPart) { 793 result.add(entry.getKey()); 794 } 795 } 796 return (IWorkbenchPart[]) result.toArray(new IWorkbenchPart[result.size()]); 797 } 798 799 } 800 | Popular Tags |