/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.diff.tools.binary;
import com.intellij.diff.DiffContext;
import com.intellij.diff.actions.impl.FocusOppositePaneAction;
import com.intellij.diff.contents.DiffContent;
import com.intellij.diff.contents.FileContent;
import com.intellij.diff.requests.ContentDiffRequest;
import com.intellij.diff.requests.DiffRequest;
import com.intellij.diff.tools.holders.BinaryEditorHolder;
import com.intellij.diff.tools.util.DiffNotifications;
import com.intellij.diff.tools.util.StatusPanel;
import com.intellij.diff.tools.util.TransferableFileEditorStateSupport;
import com.intellij.diff.tools.util.side.TwosideDiffViewer;
import com.intellij.diff.util.DiffUtil;
import com.intellij.diff.util.Side;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.AnSeparator;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.intellij.diff.util.DiffUtil.getDiffSettings;
public class TwosideBinaryDiffViewer extends TwosideDiffViewer<BinaryEditorHolder> {
public static final Logger LOG = Logger.getInstance(TwosideBinaryDiffViewer.class);
@NotNull private final TransferableFileEditorStateSupport myTransferableStateSupport;
@NotNull private final StatusPanel myStatusPanel;
public TwosideBinaryDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
super(context, (ContentDiffRequest)request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
myStatusPanel = new StatusPanel();
new MyFocusOppositePaneAction().install(myPanel);
myContentPanel.setTopAction(new MyAcceptSideAction(Side.LEFT));
myContentPanel.setBottomAction(new MyAcceptSideAction(Side.RIGHT));
myTransferableStateSupport = new TransferableFileEditorStateSupport(getDiffSettings(context), getEditorHolders(), this);
}
@Override
protected void processContextHints() {
super.processContextHints();
myTransferableStateSupport.processContextHints(myRequest, myContext);
}
@Override
protected void updateContextHints() {
super.updateContextHints();
myTransferableStateSupport.updateContextHints(myRequest, myContext);
}
@Override
protected List<AnAction> createToolbarActions() {
List<AnAction> group = new ArrayList<>();
group.add(new MyAcceptSideAction(Side.LEFT));
group.add(new MyAcceptSideAction(Side.RIGHT));
group.add(AnSeparator.getInstance());
group.add(myTransferableStateSupport.createToggleAction());
group.addAll(super.createToolbarActions());
return group;
}
//
// Diff
//
@Override
protected void onSlowRediff() {
super.onSlowRediff();
myStatusPanel.setBusy(true);
}
@Override
@NotNull
protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
try {
indicator.checkCanceled();
List<DiffContent> contents = myRequest.getContents();
if (!(contents.get(0) instanceof FileContent) || !(contents.get(1) instanceof FileContent)) {
return applyNotification(null);
}
final VirtualFile file1 = ((FileContent)contents.get(0)).getFile();
final VirtualFile file2 = ((FileContent)contents.get(1)).getFile();
final JComponent notification = ReadAction.compute(() -> {
if (!file1.isValid() || !file2.isValid()) {
return DiffNotifications.createError();
}
try {
// we can't use getInputStream() here because we can't restore BOM marker
// (getBom() can return null for binary files, while getInputStream() strips BOM for all files).
// It can be made for files from VFS that implements FileSystemInterface though.
byte[] bytes1 = file1.contentsToByteArray();
byte[] bytes2 = file2.contentsToByteArray();
return Arrays.equals(bytes1, bytes2) ? DiffNotifications.createEqualContents() : null;
}
catch (IOException e) {
LOG.warn(e);
return null;
}
});
return applyNotification(notification);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Throwable e) {
LOG.error(e);
return applyNotification(DiffNotifications.createError());
}
}
@NotNull
private Runnable applyNotification(@Nullable final JComponent notification) {
return () -> {
clearDiffPresentation();
if (notification != null) myPanel.addNotification(notification);
};
}
private void clearDiffPresentation() {
myStatusPanel.setBusy(false);
myPanel.resetNotifications();
}
//
// Getters
//
@NotNull
FileEditor getCurrentEditor() {
return getCurrentEditorHolder().getEditor();
}
@NotNull
@Override
protected JComponent getStatusPanel() {
return myStatusPanel;
}
//
// Misc
//
public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
return TwosideDiffViewer.canShowRequest(context, request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
}
//
// Actions
//
private class MyAcceptSideAction extends DumbAwareAction {
@NotNull private final Side myBaseSide;
public MyAcceptSideAction(@NotNull Side baseSide) {
myBaseSide = baseSide;
getTemplatePresentation().setText("Copy Content to " + baseSide.select("Right", "Left"));
getTemplatePresentation().setIcon(baseSide.select(AllIcons.Vcs.Arrow_right, AllIcons.Vcs.Arrow_left));
setShortcutSet(ActionManager.getInstance().getAction(baseSide.select("Diff.ApplyLeftSide", "Diff.ApplyRightSide")).getShortcutSet());
}
@Override
public void update(AnActionEvent e) {
VirtualFile baseFile = getContentFile(myBaseSide);
VirtualFile targetFile = getContentFile(myBaseSide.other());
boolean enabled = baseFile != null && targetFile != null && targetFile.isWritable();
e.getPresentation().setEnabledAndVisible(enabled);
}
@Override
public void actionPerformed(AnActionEvent e) {
final VirtualFile baseFile = getContentFile(myBaseSide);
final VirtualFile targetFile = getContentFile(myBaseSide.other());
assert baseFile != null && targetFile != null;
try {
WriteAction.run(() -> {
targetFile.setBinaryContent(baseFile.contentsToByteArray());
});
}
catch (IOException err) {
LOG.warn(err);
Messages.showErrorDialog(getProject(), err.getMessage(), "Can't Copy File");
}
}
@Nullable
private VirtualFile getContentFile(@NotNull Side side) {
DiffContent content = side.select(myRequest.getContents());
VirtualFile file = content instanceof FileContent ? ((FileContent)content).getFile() : null;
return file != null && file.isValid() ? file : null;
}
}
private class MyFocusOppositePaneAction extends FocusOppositePaneAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
setCurrentSide(getCurrentSide().other());
DiffUtil.requestFocus(getProject(), getPreferredFocusedComponent());
}
}
}