fixed #64617: Not prompted to save dirty compare editor
diff --git a/bundles/org.eclipse.compare/buildnotes_compare.html b/bundles/org.eclipse.compare/buildnotes_compare.html
index 9739fbb..1b009f2 100644
--- a/bundles/org.eclipse.compare/buildnotes_compare.html
+++ b/bundles/org.eclipse.compare/buildnotes_compare.html
@@ -26,6 +26,7 @@
 <a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=56875">56875</a>: Resizing Apply Patch dialog doesn't resize tree<br>
 <a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=47640">47640</a>: Move right to left misplaced the moved text<br>
 <a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=66272">66272</a>: remove packages prefixes tag from plugin.xml<br>
+<a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=64617">64617</a>: Not prompted to save dirty compare editor<br>
 
 
 <h1>
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
index 9b44232..2a92e0b 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
@@ -13,6 +13,7 @@
 import java.lang.reflect.InvocationTargetException;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.ResourceBundle;
 
 import org.eclipse.swt.SWT;
@@ -21,6 +22,7 @@
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.custom.BusyIndicator;
 
+import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.*;
 import org.eclipse.ui.IPersistableElement;
 import org.eclipse.ui.IEditorInput;
@@ -180,6 +182,22 @@
 				);
 			return fNavigator;
 		}
+		if (IFile.class.equals(adapter)) {
+		    IProgressMonitor pm= new NullProgressMonitor();
+			// flush changes in any dirty viewer
+			try {
+	            flushViewer(fStructureInputPane, pm);
+	            flushViewer(fStructurePane1, pm);
+	            flushViewer(fStructurePane2, pm);
+	            flushViewer(fContentInputPane, pm);
+	        } catch (CoreException e) {
+	            CompareUIPlugin.log(e);
+	        }
+		    IFile[] files= (IFile[]) getAdapter(IFile[].class);
+		    if (files.length > 0)
+		        return files[0];	// can only return one: limitation on IDE.saveAllEditors; see #64617
+		    return null;
+		}
 		return null;
 	}
 	
@@ -727,7 +745,7 @@
 			fDirtyViewers.add(source);
 		else
 			fDirtyViewers.remove(source);
-		boolean newDirty= fDirtyViewers.size() > 0;
+		boolean newDirty= fDirty || fDirtyViewers.size() > 0;
 		if (DEBUG) System.out.println("setDirty("+source+", "+dirty+"): " + newDirty); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 		if (oldDirty != newDirty)
 			Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, new Boolean(oldDirty), new Boolean(newDirty));
@@ -779,7 +797,7 @@
 
 		save(pm);
 	}
-	
+		
 	private static void flushViewer(CompareViewerSwitchingPane pane, IProgressMonitor pm) throws CoreException {
 		if (pane != null) {
 			Viewer v= pane.getViewer();
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
index 80d36bf..8ed6164 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
@@ -33,7 +33,14 @@
 	public BufferedResourceNode(IResource resource) {
 		super(resource);
 	}
-			
+	
+    /**
+     * Returns <code>true</code> if buffer contains uncommitted changes.
+     */
+	public boolean isDirty() {
+	    return fDirty;
+	}
+	
 	protected IStructureComparator createChild(IResource child) {
 		return new BufferedResourceNode(child);
 	}
@@ -58,18 +65,6 @@
 			if (resource instanceof IFile) {
 
 				byte[] bytes= getContent();
-				/*
-				String enc1= getEncoding();
-				String enc2= Utilities.getCharset((IFile)resource);
-				if (! enc1.equals(enc2)) {
-					try {
-						String content= new String(bytes, enc1);
-						bytes= content.getBytes(enc2);
-					} catch (UnsupportedEncodingException e) {
-						// ignore
-					}
-				}
-				*/
 				ByteArrayInputStream is= new ByteArrayInputStream(bytes);
 				try {
 					IFile file= (IFile) resource;
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
index e26156d..38462d8 100644
--- a/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
@@ -12,6 +12,10 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 import org.eclipse.core.resources.*;
 import org.eclipse.core.runtime.*;
@@ -366,6 +370,53 @@
 		}
 	}
 	
+	/* (non Javadoc)
+	 * see IAdaptable.getAdapter
+	 */
+	public Object getAdapter(Class adapter) {
+		if (IFile[].class.equals(adapter)) {
+		    HashSet collector= new HashSet();
+		    collectDirtyResources(fRoot, collector);
+		    return (IFile[]) collector.toArray(new IFile[collector.size()]);
+		}
+		return super.getAdapter(adapter);
+	}
+	
+	private void collectDirtyResources(Object o, Set collector) {
+		if (o instanceof DiffNode) {
+		    DiffNode node= (DiffNode) o;
+			
+			ITypedElement left= node.getLeft();
+			if (left instanceof BufferedResourceNode) {
+			    BufferedResourceNode bn= (BufferedResourceNode) left;
+			    if (bn.isDirty()) {
+			        IResource resource= bn.getResource();
+			        if (resource instanceof IFile)
+			            collector.add(resource);
+			    }
+			}
+
+			ITypedElement right= node.getRight();
+			if (right instanceof BufferedResourceNode) {
+			    BufferedResourceNode bn= (BufferedResourceNode) right;
+			    if (bn.isDirty()) {
+			        IResource resource= bn.getResource();
+			        if (resource instanceof IFile)
+			            collector.add(resource);
+			    }
+			}
+				
+			IDiffElement[] children= node.getChildren();
+			if (children != null) {
+				for (int i= 0; i < children.length; i++) {
+					IDiffElement element= children[i];
+					if (element instanceof DiffNode)
+					    collectDirtyResources(element, collector);
+				}
+			}
+		}
+	}
+	
 	private static String normalizeCase(String s) {
 		if (NORMALIZE_CASE && s != null)
 			return s.toUpperCase();
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/buildnotes_compare.html b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/buildnotes_compare.html
index 9739fbb..1b009f2 100644
--- a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/buildnotes_compare.html
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/buildnotes_compare.html
@@ -26,6 +26,7 @@
 <a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=56875">56875</a>: Resizing Apply Patch dialog doesn't resize tree<br>
 <a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=47640">47640</a>: Move right to left misplaced the moved text<br>
 <a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=66272">66272</a>: remove packages prefixes tag from plugin.xml<br>
+<a href="http://dev.eclipse.org/bugs/show_bug.cgi?id=64617">64617</a>: Not prompted to save dirty compare editor<br>
 
 
 <h1>
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
index 9b44232..2a92e0b 100644
--- a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/CompareEditorInput.java
@@ -13,6 +13,7 @@
 import java.lang.reflect.InvocationTargetException;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.ResourceBundle;
 
 import org.eclipse.swt.SWT;
@@ -21,6 +22,7 @@
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.custom.BusyIndicator;
 
+import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.*;
 import org.eclipse.ui.IPersistableElement;
 import org.eclipse.ui.IEditorInput;
@@ -180,6 +182,22 @@
 				);
 			return fNavigator;
 		}
+		if (IFile.class.equals(adapter)) {
+		    IProgressMonitor pm= new NullProgressMonitor();
+			// flush changes in any dirty viewer
+			try {
+	            flushViewer(fStructureInputPane, pm);
+	            flushViewer(fStructurePane1, pm);
+	            flushViewer(fStructurePane2, pm);
+	            flushViewer(fContentInputPane, pm);
+	        } catch (CoreException e) {
+	            CompareUIPlugin.log(e);
+	        }
+		    IFile[] files= (IFile[]) getAdapter(IFile[].class);
+		    if (files.length > 0)
+		        return files[0];	// can only return one: limitation on IDE.saveAllEditors; see #64617
+		    return null;
+		}
 		return null;
 	}
 	
@@ -727,7 +745,7 @@
 			fDirtyViewers.add(source);
 		else
 			fDirtyViewers.remove(source);
-		boolean newDirty= fDirtyViewers.size() > 0;
+		boolean newDirty= fDirty || fDirtyViewers.size() > 0;
 		if (DEBUG) System.out.println("setDirty("+source+", "+dirty+"): " + newDirty); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 		if (oldDirty != newDirty)
 			Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, new Boolean(oldDirty), new Boolean(newDirty));
@@ -779,7 +797,7 @@
 
 		save(pm);
 	}
-	
+		
 	private static void flushViewer(CompareViewerSwitchingPane pane, IProgressMonitor pm) throws CoreException {
 		if (pane != null) {
 			Viewer v= pane.getViewer();
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
index 80d36bf..8ed6164 100644
--- a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/BufferedResourceNode.java
@@ -33,7 +33,14 @@
 	public BufferedResourceNode(IResource resource) {
 		super(resource);
 	}
-			
+	
+    /**
+     * Returns <code>true</code> if buffer contains uncommitted changes.
+     */
+	public boolean isDirty() {
+	    return fDirty;
+	}
+	
 	protected IStructureComparator createChild(IResource child) {
 		return new BufferedResourceNode(child);
 	}
@@ -58,18 +65,6 @@
 			if (resource instanceof IFile) {
 
 				byte[] bytes= getContent();
-				/*
-				String enc1= getEncoding();
-				String enc2= Utilities.getCharset((IFile)resource);
-				if (! enc1.equals(enc2)) {
-					try {
-						String content= new String(bytes, enc1);
-						bytes= content.getBytes(enc2);
-					} catch (UnsupportedEncodingException e) {
-						// ignore
-					}
-				}
-				*/
 				ByteArrayInputStream is= new ByteArrayInputStream(bytes);
 				try {
 					IFile file= (IFile) resource;
diff --git a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
index e26156d..38462d8 100644
--- a/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
+++ b/bundles/org.eclipse.compare/plugins/org.eclipse.compare/compare/org/eclipse/compare/internal/ResourceCompareInput.java
@@ -12,6 +12,10 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 import org.eclipse.core.resources.*;
 import org.eclipse.core.runtime.*;
@@ -366,6 +370,53 @@
 		}
 	}
 	
+	/* (non Javadoc)
+	 * see IAdaptable.getAdapter
+	 */
+	public Object getAdapter(Class adapter) {
+		if (IFile[].class.equals(adapter)) {
+		    HashSet collector= new HashSet();
+		    collectDirtyResources(fRoot, collector);
+		    return (IFile[]) collector.toArray(new IFile[collector.size()]);
+		}
+		return super.getAdapter(adapter);
+	}
+	
+	private void collectDirtyResources(Object o, Set collector) {
+		if (o instanceof DiffNode) {
+		    DiffNode node= (DiffNode) o;
+			
+			ITypedElement left= node.getLeft();
+			if (left instanceof BufferedResourceNode) {
+			    BufferedResourceNode bn= (BufferedResourceNode) left;
+			    if (bn.isDirty()) {
+			        IResource resource= bn.getResource();
+			        if (resource instanceof IFile)
+			            collector.add(resource);
+			    }
+			}
+
+			ITypedElement right= node.getRight();
+			if (right instanceof BufferedResourceNode) {
+			    BufferedResourceNode bn= (BufferedResourceNode) right;
+			    if (bn.isDirty()) {
+			        IResource resource= bn.getResource();
+			        if (resource instanceof IFile)
+			            collector.add(resource);
+			    }
+			}
+				
+			IDiffElement[] children= node.getChildren();
+			if (children != null) {
+				for (int i= 0; i < children.length; i++) {
+					IDiffElement element= children[i];
+					if (element instanceof DiffNode)
+					    collectDirtyResources(element, collector);
+				}
+			}
+		}
+	}
+	
 	private static String normalizeCase(String s) {
 		if (NORMALIZE_CASE && s != null)
 			return s.toUpperCase();