From 58b82c868c13642466c2c7868f1f1ae563ceff43 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Fri, 26 Aug 2011 15:07:41 +0200 Subject: [PATCH] support operations in DirDiff --- .../com/intellij/ide/diff/DiffElement.java | 34 +++++++ .../ide/diff/VirtualFileDiffElement.java | 59 +++++++++++++ .../intellij/openapi/diff/impl/dir/DTree.java | 1 + .../openapi/diff/impl/dir/DirDiffElement.java | 32 ++++--- .../openapi/diff/impl/dir/DirDiffPanel.java | 34 +++---- .../diff/impl/dir/DirDiffTableModel.java | 88 +++++++++++++++++-- .../diff/impl/dir/actions/DirDiffAction.java | 6 +- .../dir/actions/DirDiffToolbarActions.java | 4 +- .../dir/actions/RefreshDirDiffAction.java | 5 ++ .../impl/dir/actions/SynchronizeDiff.java | 82 +++++++++++++++++ 10 files changed, 308 insertions(+), 37 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/SynchronizeDiff.java diff --git a/platform/platform-api/src/com/intellij/ide/diff/DiffElement.java b/platform/platform-api/src/com/intellij/ide/diff/DiffElement.java index 86670dcf3ea6..e9b5c683bc70 100644 --- a/platform/platform-api/src/com/intellij/ide/diff/DiffElement.java +++ b/platform/platform-api/src/com/intellij/ide/diff/DiffElement.java @@ -218,4 +218,38 @@ public abstract class DiffElement implements Disposable { public DataProvider getDataProvider(Project project) { return null; } + + /** + * Defines is it possible to perform such operations as copy or delete through Diff Panel + * + * @return true if copy, delete, etc operations are allowed, + * false otherwise + */ + public boolean isOperationsEnabled() { + return false; + } + + /** + * Copies element to the container. + * + * @param container file directory or other container + * @param relativePath + * @return true if coping was completed successfully, + * false otherwise + */ + public boolean copyTo(DiffElement container, String relativePath) { + return false; + } + + /** + * Deletes element + * @return true if deletion was completed successfully, + * false otherwise + */ + public boolean delete() { + return false; + } + + public void refresh() { + } } diff --git a/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java b/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java index c0eac988066c..7f010895e908 100644 --- a/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java +++ b/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java @@ -26,6 +26,7 @@ import com.intellij.openapi.fileEditor.FileEditorProvider; import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NonNls; @@ -33,6 +34,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.Callable; @@ -175,4 +177,61 @@ public class VirtualFileDiffElement extends DiffElement { } }; } + + @Override + public boolean isOperationsEnabled() { + return true; + } + + @Override + public boolean copyTo(DiffElement container, String relativePath) { + try { + final File src = new File(myFile.getPath()); + final File trg = new File(container.getValue().getPath() + relativePath + src.getName()); + FileUtil.copy(src, trg); + //final VirtualFile targetRoot = container.getValue(); + //if (targetRoot != null && targetRoot.isDirectory()) { + // VirtualFile target = getSeparator().equals(relativePath) ? targetRoot : targetRoot.findFileByRelativePath(relativePath); + // final String path; + // if (target == null) { + // path = (targetRoot.getPath() + relativePath).replace('/', File.separatorChar); + // new File(path).mkdirs(); + // target = targetRoot.findFileByRelativePath(removeSeparators(relativePath)); + // } + // + // if (target != null && target.isDirectory()) { + // myFile.copy(this, target, myFile.getName()); + // } + // else { + // assert false : "Can't find " + container.getPath() + relativePath; + // } + // + //} + } + catch (IOException e) {// + } + return false; + } + + private String removeSeparators(String path) { + final int start = path.startsWith(getSeparator()) ? getSeparator().length() : 0; + final int end = path.endsWith(getSeparator()) ? path.length() - getSeparator().length() : path.length(); + return path.substring(start, end); + } + + @Override + public boolean delete() { + try { + myFile.delete(this); + } + catch (IOException e) { + return false; + } + return true; + } + + @Override + public void refresh() { + myFile.refresh(false, true); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DTree.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DTree.java index 11ce50138c51..b6c5d7fe920f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DTree.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DTree.java @@ -64,6 +64,7 @@ public class DTree { isContainer = container; } + @NotNull public Collection getChildren() { init(); if (myChildrenList == null) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffElement.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffElement.java index 127a8a7c9a55..a309dfcce911 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffElement.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffElement.java @@ -28,6 +28,7 @@ import static com.intellij.openapi.diff.impl.dir.DirDiffOperation.*; * @author Konstantin Bulenkov */ public class DirDiffElement { + private final DTree myParent; private final DType myType; private final DiffElement mySource; private final long mySourceLength; @@ -37,7 +38,8 @@ public class DirDiffElement { private DirDiffOperation myOperation; private DirDiffOperation myDefaultOperation; - private DirDiffElement(@Nullable DiffElement source, @Nullable DiffElement target, DType type, String name) { + private DirDiffElement(DTree parent, @Nullable DiffElement source, @Nullable DiffElement target, DType type, String name) { + myParent = parent; myType = type; mySource = source; mySourceLength = source == null || source.isContainer() ? -1 : source.getSize(); @@ -75,28 +77,28 @@ public class DirDiffElement { return timeStamp < 0 ? "" : DateFormatUtil.formatDateTime(timeStamp); } - public static DirDiffElement createChange(@NotNull DiffElement source, @NotNull DiffElement target) { - return new DirDiffElement(source, target, DType.CHANGED, source.getName()); + public static DirDiffElement createChange(DTree parent, @NotNull DiffElement source, @NotNull DiffElement target) { + return new DirDiffElement(parent, source, target, DType.CHANGED, source.getName()); } - public static DirDiffElement createError(@Nullable DiffElement source, @Nullable DiffElement target) { - return new DirDiffElement(source, target, DType.ERROR, source == null ? target.getName() : source.getName()); + public static DirDiffElement createError(DTree parent, @Nullable DiffElement source, @Nullable DiffElement target) { + return new DirDiffElement(parent, source, target, DType.ERROR, source == null ? target.getName() : source.getName()); } - public static DirDiffElement createSourceOnly(@NotNull DiffElement source) { - return new DirDiffElement(source, null, DType.SOURCE, null); + public static DirDiffElement createSourceOnly(DTree parent, @NotNull DiffElement source) { + return new DirDiffElement(parent, source, null, DType.SOURCE, null); } - public static DirDiffElement createTargetOnly(@NotNull DiffElement target) { - return new DirDiffElement(null, target, DType.TARGET, null); + public static DirDiffElement createTargetOnly(DTree parent, @NotNull DiffElement target) { + return new DirDiffElement(parent, null, target, DType.TARGET, null); } - public static DirDiffElement createDirElement(DiffElement src, DiffElement trg, String name) { - return new DirDiffElement(src, trg, DType.SEPARATOR, name); + public static DirDiffElement createDirElement(DTree parent, DiffElement src, DiffElement trg, String name) { + return new DirDiffElement(parent, src, trg, DType.SEPARATOR, name); } - public static DirDiffElement createEqual(@NotNull DiffElement source, @NotNull DiffElement target) { - return new DirDiffElement(source, target, DType.EQUAL, source.getName()); + public static DirDiffElement createEqual(DTree parent, @NotNull DiffElement source, @NotNull DiffElement target) { + return new DirDiffElement(parent, source, target, DType.EQUAL, source.getName()); } public DType getType() { @@ -167,4 +169,8 @@ public class DirDiffElement { public Icon getIcon() { return mySource != null ? mySource.getIcon() : myTarget.getIcon(); } + + public DTree getParentNode() { + return myParent; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java index 81c475e77afd..8874dd28b38f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java @@ -109,25 +109,27 @@ public class DirDiffPanel implements Disposable { myDiffWindow.setTitle(myModel.getTitle()); } }); - new AnAction("Change diff operation") { - @Override - public void actionPerformed(AnActionEvent e) { - changeOperationForSelectedRow(); - } - }.registerCustomShortcutSet(CustomShortcutSet.fromString("SPACE"), myTable); - myTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (myTable.getRowCount() > 0 && e.getClickCount() > 1) { - final int row = myTable.rowAtPoint(e.getPoint()); - final int col = myTable.columnAtPoint(e.getPoint()); + if (model.isOperationsEnabled()) { + new AnAction("Change diff operation") { + @Override + public void actionPerformed(AnActionEvent e) { + changeOperationForSelectedRow(); + } + }.registerCustomShortcutSet(CustomShortcutSet.fromString("SPACE"), myTable); + myTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (myTable.getRowCount() > 0 && e.getClickCount() > 1) { + final int row = myTable.rowAtPoint(e.getPoint()); + final int col = myTable.columnAtPoint(e.getPoint()); - if (row != -1 && col == ((myTable.getColumnCount() - 1) / 2)) { - changeOperationForSelectedRow(); + if (row != -1 && col == ((myTable.getColumnCount() - 1) / 2)) { + changeOperationForSelectedRow(); + } } } - } - }); + }); + } myTable.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffTableModel.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffTableModel.java index 95ebb8b9ca04..08a740e9b46f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffTableModel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffTableModel.java @@ -19,6 +19,7 @@ import com.intellij.ide.diff.DiffElement; import com.intellij.ide.diff.DirDiffModel; import com.intellij.ide.diff.DirDiffSettings; import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; @@ -133,6 +134,14 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode getSettings().setFilter(myPanel.getFilter()); } + public boolean isOperationsEnabled() { + return mySrc.isOperationsEnabled() && myTrg.isOperationsEnabled(); + } + + public List getElements() { + return myElements; + } + private static String prepareText(String text) { final int LEN = EMPTY_STRING.length(); String right; @@ -165,15 +174,19 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode } public void reloadModel() { + //System.out.println("Start reloading"); myUpdating.set(true); final JBLoadingPanel loadingPanel = getLoadingPanel(); loadingPanel.startLoading(); ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { + //System.out.println("start updating on pooled thread"); try { updater = new Updater(loadingPanel, 100); updater.start(); myTree = new DTree(null, "", true); + mySrc.refresh(); + myTrg.refresh(); scan(mySrc, myTree, true); scan(myTrg, myTree, false); @@ -184,6 +197,7 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode } catch (Exception e) {// } + //System.out.println("end updating on pooled thread"); } }); } @@ -193,6 +207,7 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode } public void applySettings() { + //System.out.println(StringUtil.getThrowableText(new Throwable())); if (! myUpdating.get()) myUpdating.set(true); final JBLoadingPanel loadingPanel = getLoadingPanel(); if (!loadingPanel.isLoading()) { @@ -205,6 +220,7 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode final Application app = ApplicationManager.getApplication(); app.executeOnPooledThread(new Runnable() { public void run() { + //System.out.println("start updating tree"); myTree.updateVisibility(mySettings); final ArrayList elements = new ArrayList(); fillElements(myTree, elements); @@ -227,6 +243,7 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode } else { app.invokeLater(uiThread); } + //System.out.println("stop updating tree"); } }); } @@ -240,24 +257,24 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode if (!child.isContainer()) { if (child.isVisible()) { if (!separatorAdded) { - elements.add(DirDiffElement.createDirElement(tree.getSource(), tree.getTarget(), tree.getPath())); + elements.add(DirDiffElement.createDirElement(tree, tree.getSource(), tree.getTarget(), tree.getPath())); separatorAdded = true; } switch (child.getType()) { case SOURCE: - elements.add(DirDiffElement.createSourceOnly(child.getSource())); + elements.add(DirDiffElement.createSourceOnly(tree, child.getSource())); break; case TARGET: - elements.add(DirDiffElement.createTargetOnly(child.getTarget())); + elements.add(DirDiffElement.createTargetOnly(tree, child.getTarget())); break; case CHANGED: - elements.add(DirDiffElement.createChange(child.getSource(), child.getTarget())); + elements.add(DirDiffElement.createChange(tree, child.getSource(), child.getTarget())); break; case EQUAL: - elements.add(DirDiffElement.createEqual(child.getSource(), child.getTarget())); + elements.add(DirDiffElement.createEqual(tree, child.getSource(), child.getTarget())); break; case ERROR: - elements.add(DirDiffElement.createError(child.getSource(), child.getTarget())); + elements.add(DirDiffElement.createError(tree, child.getSource(), child.getTarget())); } } } else { @@ -354,6 +371,17 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode } return ""; } + + public List getSelectedElements() { + final int[] rows = myTable.getSelectedRows(); + final ArrayList elements = new ArrayList(); + for (int row : rows) { + final DirDiffElement element = getElementAt(row); + if (element == null || element.isSeparator()) continue; + elements.add(element); + } + return elements; + } @Override public String getColumnName(int column) { @@ -427,6 +455,54 @@ public class DirDiffTableModel extends AbstractTableModel implements DirDiffMode return mySettings; } + public void performCopyTo(DirDiffElement element) { + final DiffElement source = element.getSource(); + if (source != null) { + final String path = element.getParentNode().getPath(); + + final AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass()); + try { + source.copyTo(myTrg, path); + } finally { + token.finish(); + } + } + } + + public void performCopyFrom(DirDiffElement element) { + final DiffElement target = element.getTarget(); + if (target != null) { + final String path = element.getParentNode().getPath(); + final AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass()); + try { + target.copyTo(mySrc, path); + } finally { + token.finish(); + } + } + } + + public void performDelete(DirDiffElement element) { + final DiffElement source = element.getSource(); + final DiffElement target = element.getTarget(); + final int index = myElements.indexOf(element); + if (index != -1) { + myElements.remove(index); + fireTableRowsDeleted(index, index); + } + final AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass()); + try { + if (source != null) { + source.delete(); + } + if (target != null) { + target.delete(); + } + } finally { + token.finish(); + } + } + class Updater extends Thread { private final JBLoadingPanel myLoadingPanel; private final int mySleep; diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java index df0c6cb99b0d..3107befd6b95 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffAction.java @@ -43,7 +43,7 @@ public abstract class DirDiffAction extends ToggleAction implements DirDiffIcons @Override public final void setSelected(AnActionEvent e, boolean state) { updateState(state); - if (this.getClass() == RefreshDirDiffAction.class) { + if (isFullReload()) { getModel().reloadModel(); } else { if (state) { @@ -54,6 +54,10 @@ public abstract class DirDiffAction extends ToggleAction implements DirDiffIcons } } + protected boolean isFullReload() { + return false; + } + @Override public void update(AnActionEvent e) { super.update(e); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffToolbarActions.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffToolbarActions.java index 6d99192a59f3..6fbca1be50d7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffToolbarActions.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/DirDiffToolbarActions.java @@ -43,7 +43,9 @@ public class DirDiffToolbarActions extends ActionGroup { new EnableRight(model), Separator.getInstance(), new ChangeCompareModeGroup(model), - Separator.getInstance())); + Separator.getInstance(), + new SynchronizeDiff(model, false), + new SynchronizeDiff(model, true))); for (AnAction action : model.getSettings().getExtraActions()) { actions.add(action); diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/RefreshDirDiffAction.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/RefreshDirDiffAction.java index e6b8233a7978..3b32ac740cef 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/RefreshDirDiffAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/RefreshDirDiffAction.java @@ -43,4 +43,9 @@ public class RefreshDirDiffAction extends DirDiffAction { public ShortcutSet getShortcut() { return CustomShortcutSet.fromString("F5"); } + + @Override + protected boolean isFullReload() { + return true; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/SynchronizeDiff.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/SynchronizeDiff.java new file mode 100644 index 000000000000..fcc6beb9951e --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/actions/SynchronizeDiff.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2011 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.openapi.diff.impl.dir.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.actionSystem.ShortcutSet; +import com.intellij.openapi.diff.impl.dir.DirDiffElement; +import com.intellij.openapi.diff.impl.dir.DirDiffOperation; +import com.intellij.openapi.diff.impl.dir.DirDiffTableModel; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.SystemInfo; + +import java.util.List; + +/** + * @author Konstantin Bulenkov + */ +public class SynchronizeDiff extends DirDiffAction { + private boolean mySelectedOnly; + + public SynchronizeDiff(DirDiffTableModel model, boolean selectedOnly) { + super(model, + selectedOnly ? "Synchronize Selected" : "Synchronize All", + IconLoader.getIcon(selectedOnly ? "/actions/resume.png" : "/actions/refreshUsages.png")); + mySelectedOnly = selectedOnly; + } + + @Override + protected void updateState(boolean state) { + final List elements = mySelectedOnly ? getModel().getSelectedElements() : getModel().getElements(); + for (DirDiffElement element : elements) { + final DirDiffOperation operation = element.getOperation(); + if (operation == null) continue; + switch (operation) { + case COPY_TO: + getModel().performCopyTo(element); + break; + case COPY_FROM: + getModel().performCopyFrom(element); + break; + case MERGE: + break; + case EQUAL: + break; + case NONE: + break; + case DELETE: + getModel().performDelete(element); + break; + } + } + } + + @Override + public ShortcutSet getShortcut() { + return CustomShortcutSet.fromString(mySelectedOnly ? "ENTER" : SystemInfo.isMac ? "meta ENTER" : "control ENTER"); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return false; + } + + @Override + protected boolean isFullReload() { + return true; + } +}