Diff for binary files

This commit is contained in:
Konstantin Bulenkov
2011-04-27 16:16:46 +04:00
parent 2ee71764bf
commit 7821abc29b
10 changed files with 339 additions and 72 deletions
@@ -28,6 +28,7 @@ import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
@@ -108,6 +109,12 @@ public class ChangesDiffCalculator {
@Override
public void highlightText(Fragment fragment, boolean drawBorder) {
}
@Override
public FileEditor getFileEditor() {
return null;
}
}
private static class ChangesCollector extends BaseMarkup {
@@ -128,6 +135,5 @@ public class ChangesDiffCalculator {
ranges.add(currentRange);
}
}
}
}
@@ -41,6 +41,7 @@ import com.intellij.openapi.editor.ScrollingModel;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.EditorMarkupModel;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
@@ -149,6 +150,10 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
}
}
public void removeStatusBar() {
myPanel.removeStatusBar();
}
private static DiffHighlighterFactory createHighlighter(FileType contentType, VirtualFile file, Project project) {
return new DiffHighlighterFactoryImpl(contentType, file, project);
}
@@ -246,11 +251,12 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
public void onContentChangedIn(EditorSource source) {
myDiffUpdater.contentRemoved(source);
final EditorEx editor = source.getEditor();
final FileEditor fileEditor = source.getFileEditor();
if (source.getSide() == FragmentSide.SIDE1 && editor != null) {
editor.setVerticalScrollbarOrientation(EditorEx.VERTICAL_SCROLLBAR_LEFT);
}
DiffSideView viewSide = getSideView(source.getSide());
viewSide.setEditorSource(source);
viewSide.setEditorSource(getProject(), source);
Disposer.dispose(myScrollSupport);
if (editor == null) {
if (!myDisposed) {
@@ -350,6 +356,9 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
final JComponent newBottomComponent = data.getBottomComponent();
myPanel.setBottomComponent(newBottomComponent);
if (data.getContents()[0].isBinary() || data.getContents()[1].isBinary()) {
myPanel.removeStatusBar();
}
if (myIsRequestFocus) {
if ((isEditor1Focused || isEditor2Focused)) {
@@ -15,7 +15,10 @@
*/
package com.intellij.openapi.diff.impl;
import com.intellij.ide.DataManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.diff.DiffContent;
import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.diff.impl.util.LabeledEditor;
@@ -28,9 +31,12 @@ import com.intellij.openapi.editor.event.EditorMouseEventArea;
import com.intellij.openapi.editor.event.EditorMouseMotionAdapter;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.util.IJSwingUtilities;
import com.intellij.util.ui.ScrollUtil;
import org.jetbrains.annotations.NonNls;
import javax.swing.*;
import java.awt.*;
@@ -64,22 +70,37 @@ public class DiffSideView {
return myPanel;
}
public void setEditorSource(EditorSource source) {
public void setEditorSource(final Project project, final EditorSource source) {
MyState state = new MyState();
myEditorSource = source;
myLineMarker.attach(myEditorSource);
Editor editor = myEditorSource.getEditor();
final FileEditor fileEditor = myEditorSource.getFileEditor();
if (editor == null) {
insertComponent(MOCK_COMPONENT);
return;
}
editor.getScrollingModel().scrollHorizontally(0);
insertComponent(editor.getComponent());
applyHighlighter();
setMouseListeners(source);
MyEditorFocusListener.install(this);
insertComponent(fileEditor == null ? MOCK_COMPONENT : fileEditor.getComponent());
DataManager.registerDataProvider(myPanel, new DataProvider() {
@Override
public Object getData(@NonNls String dataId) {
if (PlatformDataKeys.PROJECT.is(dataId)) {return project;}
if (PlatformDataKeys.FILE_EDITOR.is(dataId)) {return fileEditor;}
return null;
}
});
if (fileEditor != null) {
ScrollUtil.scrollVertically(fileEditor.getComponent(), 0);
ScrollUtil.scrollHorizontally(fileEditor.getComponent(), 0);
state.restore();
}
} else {
DataManager.removeDataProvider(myPanel);
editor.getScrollingModel().scrollHorizontally(0);
insertComponent(editor.getComponent());
applyHighlighter();
setMouseListeners(source);
MyEditorFocusListener.install(this);
state.restore();
}
}
private void insertComponent(JComponent component) {
@@ -20,30 +20,47 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.DiffContent;
import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Konstantin Bulenkov
* @author max
*/
public interface EditorSource {
FragmentSide getSide();
DiffContent getContent();
@Nullable FragmentSide getSide();
@Nullable DiffContent getContent();
@Nullable EditorEx getEditor();
@Nullable FileEditor getFileEditor();
void addDisposable(@NotNull Disposable disposable);
EditorSource NULL = new EditorSource() {
public EditorEx getEditor() {
return null;
}
public void addDisposable(Disposable disposable) {
@Override
public FileEditor getFileEditor() {
return null;
}
public void addDisposable(@NotNull Disposable disposable) {
Logger.getInstance("#com.intellij.openapi.diff.impl.EditorSource").assertTrue(false);
}
@Nullable
public FragmentSide getSide() {
return null;
}
@Nullable
public DiffContent getContent() {
return null;
}
};
EditorEx getEditor();
void addDisposable(Disposable disposable);
}
@@ -16,47 +16,100 @@
package com.intellij.openapi.diff.impl.external;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.DiffContent;
import com.intellij.openapi.diff.DiffRequest;
import com.intellij.openapi.diff.DiffTool;
import com.intellij.openapi.diff.DiffBundle;
import com.intellij.openapi.diff.*;
import com.intellij.openapi.fileEditor.FileEditorProvider;
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import javax.swing.*;
import java.io.IOException;
import java.util.Arrays;
class BinaryDiffTool implements DiffTool {
/**
* @author Konstantin Bulenkov
* @author max
*/
public class BinaryDiffTool implements DiffTool {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.external.BinaryDiffTool");
public static final DiffTool INSTANCE = new BinaryDiffTool();
public void show(DiffRequest data) {
DiffContent[] contents = data.getContents();
try {
compareBinaryFiles(contents[0].getBytes(), contents[1].getBytes());
public void show(final DiffRequest data) {
final DiffContent current = data.getContents()[0];
final DiffContent upToDate = data.getContents()[1];
final Project project = data.getProject();
if ((current instanceof FileContent && upToDate instanceof FileContent)) {
final VirtualFile src = current.getFile();
final VirtualFile trg = upToDate.getFile();
if (src != null && trg != null) {
final FileEditorProvider[] srcProvider = FileEditorProviderManager.getInstance().getProviders(project, src);
final FileEditorProvider[] trgProvider = FileEditorProviderManager.getInstance().getProviders(project, trg);
if (srcProvider.length > 0 && trgProvider.length > 0) {
new DialogWrapper(project) {
public DiffPanel myPanel;
{
setModal(false);
init();
}
@Override
protected String getDimensionServiceKey() {
return "BinaryDiffDialog";
}
@Override
protected Action[] createActions() {
final Action close = getCancelAction();
close.putValue(Action.NAME, "&Close");
return new Action[]{close};
}
@Override
protected void dispose() {
super.dispose();
Disposer.dispose(myPanel);
}
@Override
protected JComponent createCenterPanel() {
myPanel = DiffManager.getInstance().createDiffPanel(getWindow(), project);
myPanel.setDiffRequest(data);
myPanel.setTitle1(src.getPath());
myPanel.setTitle2(trg.getPath());
return myPanel.getComponent();
}
}.show();
return;
}
}
}
catch (IOException e) {
try {
final boolean equal = Arrays.equals(current.getBytes(), upToDate.getBytes());
Messages.showMessageDialog(equal
? DiffBundle.message("binary.files.are.identical.message")
: DiffBundle.message("binary.files.are.different.message"),
equal
? DiffBundle.message("files.are.identical.dialog.title")
: DiffBundle.message("files.are.different.dialog.title"),
Messages.getInformationIcon());
} catch (IOException e) {
LOG.error(e);
}
}
private static void compareBinaryFiles(byte[] currentContent, byte[] upToDateContent) {
if (Arrays.equals(currentContent, upToDateContent)) {
Messages.showMessageDialog(DiffBundle.message("binary.files.are.identical.message"),
DiffBundle.message("files.are.identical.dialog.title"), Messages.getInformationIcon());
public boolean canShow(final DiffRequest data) {
final DiffContent[] contents = data.getContents();
if (contents.length != 2) {
return false;
}
else {
Messages.showMessageDialog(DiffBundle.message("binary.files.are.different.message"),
DiffBundle.message("files.are.different.dialog.title"), Messages.getInformationIcon());
}
}
public boolean canShow(DiffRequest data) {
DiffContent[] contents = data.getContents();
if (contents.length != 2) return false;
for (int i = 0; i < contents.length; i++) {
DiffContent content = contents[i];
VirtualFile file = content.getFile();
if (file != null && file.isDirectory()) return false;
for (DiffContent content : contents) {
final VirtualFile file = content.getFile();
if (file == null || file.isDirectory()) {
return false;
}
}
return true;
}
@@ -189,7 +189,7 @@ public abstract class DiffMarkup implements EditorSource {
myDisposables.clear();
}
public void addDisposable(Disposable disposable) {
public void addDisposable(@NotNull Disposable disposable) {
myDisposables.add(disposable);
}
@@ -25,7 +25,11 @@ import com.intellij.openapi.diff.impl.util.ContentDocumentListener;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorProvider;
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.highlighting.EditorWrapper");
@@ -33,6 +37,8 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent {
private DiffContent myContent;
private final FragmentSide mySide;
private ContentChangeListener myListener = null;
private FileEditor myFileEditor;
private FileEditorProvider myFileEditorProvider;
public EditorPlaceHolder(FragmentSide side, Project project) {
super(project);
@@ -60,22 +66,45 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent {
myContent = content;
if (myContent != null) {
Document document = myContent.getDocument();
final EditorFactory editorFactory = EditorFactory.getInstance();
myEditor = DiffUtil.createEditor(document, getProject(), false);
addDisposable(new Disposable() {
public void dispose() {
editorFactory.releaseEditor(myEditor);
myEditor = null;
if (myContent.isBinary() || document == null) {
final VirtualFile file = myContent.getFile();
if (file != null) {
final FileEditorProvider[] providers = FileEditorProviderManager.getInstance().getProviders(getProject(), file);
if (providers.length > 0) {
myFileEditor = providers[0].createEditor(getProject(), file);
myFileEditorProvider = providers[0];
addDisposable(new Disposable() {
@Override
public void dispose() {
myFileEditorProvider.disposeEditor(myFileEditor);
myFileEditor = null;
myFileEditorProvider = null;
}
});
}
}
});
ContentDocumentListener.install(myContent, this);
} else {
final EditorFactory editorFactory = EditorFactory.getInstance();
myEditor = DiffUtil.createEditor(document, getProject(), false);
addDisposable(new Disposable() {
public void dispose() {
editorFactory.releaseEditor(myEditor);
myEditor = null;
}
});
ContentDocumentListener.install(myContent, this);
}
}
fireContentChanged();
}
public EditorEx getEditor() { return myEditor; }
public EditorEx getEditor() {
return myEditor;
}
public FragmentSide getSide() { return mySide; }
public FragmentSide getSide() {
return mySide;
}
public DiffContent getContent() {
return myContent;
@@ -84,4 +113,9 @@ class EditorPlaceHolder extends DiffMarkup implements DiffVersionComponent {
public void removeContent() {
setContent(null);
}
@Override
public FileEditor getFileEditor() {
return myFileEditor;
}
}
@@ -85,6 +85,10 @@ public class DiffPanelOutterComponent extends JPanel implements DataProvider {
return new Dimension(600, 400);
}
public void removeStatusBar() {
myBottomContainer.remove(myStatusBar);
}
public Object getData(String dataId) {
if (PlatformDataKeys.SOURCE_NAVIGATION_LOCKED.is(dataId)) {
return Boolean.TRUE;
@@ -16,40 +16,112 @@
package com.intellij.util.ui;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
/**
* @author mike
* @author Konstantin Bulenkov
*/
public class ScrollUtil {
private ScrollUtil() {
private ScrollUtil() {}
@Nullable
public static JScrollPane findScrollPane(JComponent c) {
if (c == null) return null;
return UIUtil.findComponentOfType(c, JScrollPane.class);
}
public static void center(JComponent c, Rectangle r) {
@Nullable
public static JScrollBar findVerticalScrollBar(JComponent c) {
return findScrollBar(c, Adjustable.VERTICAL);
}
@Nullable
public static JScrollBar findHorizontalScrollBar(JComponent c) {
return findScrollBar(c, Adjustable.HORIZONTAL);
}
@Nullable
private static JScrollBar findScrollBar(JComponent c, int orientation) {
if (c == null) return null;
if (c instanceof JScrollBar && ((JScrollBar)c).getOrientation() == orientation) {
return (JScrollBar)c;
}
for (Component comp : c.getComponents()) {
if (comp instanceof JComponent) {
final JScrollBar scrollBar = findScrollBar((JComponent)comp, orientation);
if (scrollBar != null) {
return scrollBar;
}
}
}
return null;
}
public static void scrollVertically(JComponent c, int position) {
final JScrollPane pane = findScrollPane(c);
if (pane != null) {
final JScrollBar bar = pane.getVerticalScrollBar();
if (bar != null) {
bar.setValue(position);
}
} else {
final JScrollBar scrollBar = findVerticalScrollBar(c);
if (scrollBar != null) {
scrollBar.setValue(position);
}
}
}
public static void scrollHorizontally(JComponent c, int position) {
final JScrollPane pane = findScrollPane(c);
if (pane != null) {
final JScrollBar bar = pane.getHorizontalScrollBar();
if (bar != null) {
bar.setValue(position);
}
} else {
final JScrollBar scrollBar = findHorizontalScrollBar(c);
if (scrollBar != null) {
scrollBar.setValue(position);
}
}
}
public static void center(final JComponent c, final Rectangle r) {
center(c, r, false);
}
public static void center(JComponent c, Rectangle r, boolean withInsets) {
Rectangle visible = c.getVisibleRect();
public static void center(final JComponent c, final Rectangle r, final boolean withInsets) {
final Rectangle visible = c.getVisibleRect();
visible.x = r.x - (visible.width - r.width) / 2;
visible.y = r.y - (visible.height - r.height) / 2;
Rectangle bounds = c.getBounds();
Insets i = withInsets ? new Insets(0, 0, 0, 0) : c.getInsets();
final Rectangle bounds = c.getBounds();
final Insets i = withInsets ? new Insets(0, 0, 0, 0) : c.getInsets();
bounds.x = i.left;
bounds.y = i.top;
bounds.width -= i.left + i.right;
bounds.height -= i.top + i.bottom;
if (visible.x < bounds.x) visible.x = bounds.x;
if (visible.x < bounds.x) {
visible.x = bounds.x;
}
if (visible.x + visible.width > bounds.x + bounds.width) visible.x = bounds.x + bounds.width - visible.width;
if (visible.x + visible.width > bounds.x + bounds.width) {
visible.x = bounds.x + bounds.width - visible.width;
}
if (visible.y < bounds.y) visible.y = bounds.y;
if (visible.y < bounds.y) {
visible.y = bounds.y;
}
if (visible.y + visible.height > bounds.y + bounds.height) visible.y = bounds.y + bounds.height - visible.height;
if (visible.y + visible.height > bounds.y + bounds.height) {
visible.y = bounds.y + bounds.height - visible.height;
}
c.scrollRectToVisible(visible);
}
@@ -15,19 +15,27 @@
*/
package com.intellij.openapi.vcs.history;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diff.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
import com.intellij.util.WaitForProgressToShow;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
@@ -69,7 +77,7 @@ public class VcsHistoryUtil {
* @throws com.intellij.openapi.vcs.VcsException
* @throws java.io.IOException
*/
public static void showDiff(Project project, FilePath filePath, VcsFileRevision revision1, VcsFileRevision revision2, String title1, String title2) throws VcsException, IOException {
public static void showDiff(final Project project, FilePath filePath, VcsFileRevision revision1, VcsFileRevision revision2, String title1, String title2) throws VcsException, IOException {
final byte[] content1 = loadRevisionContent(revision1);
final byte[] content2 = loadRevisionContent(revision2);
@@ -79,11 +87,54 @@ public class VcsHistoryUtil {
final Charset charset = filePath.getCharset();
final FileType fileType = filePath.getFileType();
diffData.setContentTitles(title1, title2);
diffData.setContents(createContent(project, content1, revision1, doc, charset, fileType),
createContent(project, content2, revision2, doc, charset, fileType));
final Ref<VirtualFile> f1 = new Ref<VirtualFile>(null);
final Ref<VirtualFile> f2 = new Ref<VirtualFile>(null);
if (fileType.isBinary()) {
final File file1 = FileUtil.createTempFile(revision1.getRevisionNumber().asString(), filePath.getName());
final File file2 = FileUtil.createTempFile(revision2.getRevisionNumber().asString(), filePath.getName());
try {
final FileOutputStream fos1 = new FileOutputStream(file1);
fos1.write(content1);
final FileOutputStream fos2 = new FileOutputStream(file2);
fos2.write(content2);
fos1.close();
fos2.close();
f1.set(LocalFileSystem.getInstance().findFileByIoFile(file1));
f2.set(LocalFileSystem.getInstance().findFileByIoFile(file2));
} catch(Exception e) {//
}
}
if (f1.isNull() || f2.isNull()) {
diffData.setContents(createContent(project, content1, revision1, doc, charset, fileType),
createContent(project, content2, revision2, doc, charset, fileType));
} else {
diffData.setContents(new FileContent(project, f1.get()), new FileContent(project, f2.get()));
}
WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() {
public void run() {
DiffManager.getInstance().getDiffTool().show(diffData);
if (!f1.isNull() || !f2.isNull()) {
Disposer.register(project, new Disposable() {
@Override
public void dispose() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
try {
if (!f1.isNull()) {
f1.get().delete(this);
}
if (!f2.isNull()) {
f2.get().delete(this);
}
}
catch (IOException e) {//
}
}
});
}
});
}
}
}, null, project);
}