diff --git a/plugins/svn4idea/src/META-INF/plugin.xml b/plugins/svn4idea/src/META-INF/plugin.xml index 36a06eff2ba3..8a383779a8c3 100644 --- a/plugins/svn4idea/src/META-INF/plugin.xml +++ b/plugins/svn4idea/src/META-INF/plugin.xml @@ -32,14 +32,16 @@ - + - @@ -60,7 +62,8 @@ - + @@ -86,7 +89,8 @@ - + @@ -131,16 +135,24 @@ + serviceImplementation="org.jetbrains.idea.svn.branchConfig.SvnBranchMapperManager"/> + serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.SvnRepositoryCache"/> + serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.CacheLoader"/> - + + + + + + diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index 5de8d4400e69..60e823bb16d3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -496,6 +496,7 @@ diff.options.title=Compare With Branch or Tag diff.options.no.url.error=Please select an URL to compare with diff.options.same.url.error=Please select different URLs to compare diff.options.no.patch.file.error=Please specify the path to save the patch file +diff.cant.get.properties.changes=Can't show SVN properties repository.browser.browse.changes.action=_Browse Changes... repository.browser.browse.changes.description=View history of changes in the selected node repository.browser.compare.title=Compare of ''{0} and ''{1}'' diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index 465b427b13b8..ad63a0c65386 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -33,7 +33,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction; import org.jetbrains.idea.svn.api.NodeKind; import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationManager; +import org.jetbrains.idea.svn.history.SimplePropertyRevision; import org.jetbrains.idea.svn.info.Info; +import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.status.Status; import org.jetbrains.idea.svn.status.StatusType; import org.tmatesoft.svn.core.SVNException; @@ -377,7 +379,7 @@ class SvnChangeProviderContext implements StatusReceiver { ContentRevision contentRevision = isBeforeRevision ? change.getBeforeRevision() : change.getAfterRevision(); SVNRevision revision = isBeforeRevision ? SVNRevision.BASE : SVNRevision.WORKING; - return new SimpleContentRevision(getProperties(file, revision), path, getRevisionNumber(contentRevision)); + return new SimplePropertyRevision(getProperties(file, revision), path, getRevisionNumber(contentRevision)); } @Nullable @@ -386,7 +388,7 @@ class SvnChangeProviderContext implements StatusReceiver { } @NotNull - private String getProperties(@NotNull File file, @NotNull SVNRevision revision) throws SVNException { + private List getProperties(@NotNull File file, @NotNull SVNRevision revision) throws SVNException { return AbstractShowPropertiesDiffAction.getPropertyList(myVcs, file, revision); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java index 100eb2eaaa17..99b041952665 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java @@ -16,9 +16,6 @@ package org.jetbrains.idea.svn.actions; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.diff.DiffManager; -import com.intellij.openapi.diff.SimpleContent; -import com.intellij.openapi.diff.SimpleDiffRequest; import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -27,6 +24,8 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.diff.DiffDialogHints; +import com.intellij.openapi.util.diff.DiffManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.FilePath; @@ -43,6 +42,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest.PropertyContent; import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision; import org.jetbrains.idea.svn.properties.PropertyConsumer; import org.jetbrains.idea.svn.properties.PropertyData; @@ -127,8 +128,8 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen private class CalculateAndShow extends Task.Backgroundable { private final Change myChange; - private String myBeforeContent; - private String myAfterContent; + private List myBeforeContent; + private List myAfterContent; private SVNRevision myBeforeRevisionValue; private SVNRevision myAfterRevision; private Exception myException; @@ -167,16 +168,17 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen return; } if (myBeforeContent != null && myAfterContent != null && myBeforeRevisionValue != null && myAfterRevision != null) { - final SimpleDiffRequest diffRequest = new SimpleDiffRequest(myProject, getDiffWindowTitle(myChange)); + SvnPropertiesDiffRequest diffRequest; if (compareRevisions(myBeforeRevisionValue, myAfterRevision) > 0) { - // before ahead - diffRequest.setContents(new SimpleContent(myAfterContent), new SimpleContent(myBeforeContent)); - diffRequest.setContentTitles(revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue)); + diffRequest = new SvnPropertiesDiffRequest(getDiffWindowTitle(myChange), + new PropertyContent(myAfterContent), new PropertyContent(myBeforeContent), + revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue)); } else { - diffRequest.setContents(new SimpleContent(myBeforeContent), new SimpleContent(myAfterContent)); - diffRequest.setContentTitles(revisionToString(myBeforeRevisionValue), revisionToString(myAfterRevision)); + diffRequest = new SvnPropertiesDiffRequest(getDiffWindowTitle(myChange), + new PropertyContent(myBeforeContent), new PropertyContent(myAfterContent), + revisionToString(myBeforeRevisionValue), revisionToString(myAfterRevision)); } - DiffManager.getInstance().getDiffTool().show(diffRequest); + DiffManager.getInstance().showDiff(myProject, diffRequest, DiffDialogHints.DEFAULT); } } }); @@ -225,12 +227,12 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen private final static String ourPropertiesDelimiter = "\n"; @NotNull - private static String getPropertyList(@NotNull SvnVcs vcs, - @Nullable final ContentRevision contentRevision, - @Nullable final SVNRevision revision) + private static List getPropertyList(@NotNull SvnVcs vcs, + @Nullable final ContentRevision contentRevision, + @Nullable final SVNRevision revision) throws SVNException, VcsException { if (contentRevision == null) { - return ""; + return null; } SvnTarget target; @@ -246,13 +248,13 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen } @NotNull - public static String getPropertyList(@NotNull SvnVcs vcs, @NotNull final SVNURL url, @Nullable final SVNRevision revision) + public static List getPropertyList(@NotNull SvnVcs vcs, @NotNull final SVNURL url, @Nullable final SVNRevision revision) throws VcsException { return getPropertyList(vcs, SvnTarget.fromURL(url, revision), revision); } @NotNull - public static String getPropertyList(@NotNull SvnVcs vcs, @NotNull final File ioFile, @Nullable final SVNRevision revision) + public static List getPropertyList(@NotNull SvnVcs vcs, @NotNull final File ioFile, @Nullable final SVNRevision revision) throws SVNException { try { return getPropertyList(vcs, SvnTarget.fromFile(ioFile, revision), revision); @@ -263,14 +265,14 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen } @NotNull - private static String getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision) + private static List getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision) throws VcsException { final List lines = new ArrayList(); final PropertyConsumer propertyHandler = createHandler(revision, lines); vcs.getFactory(target).createPropertyClient().list(target, revision, Depth.EMPTY, propertyHandler); - return toSortedStringPresentation(lines); + return lines; } @NotNull @@ -305,7 +307,7 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen } @NotNull - private static String toSortedStringPresentation(@NotNull List lines) { + public static String toSortedStringPresentation(@NotNull List lines) { StringBuilder sb = new StringBuilder(); Collections.sort(lines, new Comparator() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnChangeDiffRequestProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnChangeDiffRequestProvider.java new file mode 100644 index 000000000000..22edafd3d8f2 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnChangeDiffRequestProvider.java @@ -0,0 +1,126 @@ +package org.jetbrains.idea.svn.difftool; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.UserDataHolder; +import com.intellij.openapi.util.diff.chains.DiffRequestPresentableException; +import com.intellij.openapi.util.diff.impl.DiffViewerWrapper; +import com.intellij.openapi.util.diff.requests.DiffRequest; +import com.intellij.openapi.util.diff.requests.ErrorDiffRequest; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; +import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestPresentable; +import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnBundle; +import org.jetbrains.idea.svn.SvnChangeProvider; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest; +import org.jetbrains.idea.svn.history.PropertyRevision; +import org.jetbrains.idea.svn.properties.PropertyData; + +import java.util.List; +import java.util.Map; + +public class SvnChangeDiffRequestProvider implements ChangeDiffRequestProvider { + private static final Logger LOG = Logger.getInstance(SvnChangeDiffRequestProvider.class); + + @Override + public boolean canCreate(@NotNull Project project, @NotNull Change change) { + return getSvnChangeLayer(change) != null; // TODO: do not show, if no properties are set in both revisions ? + } + + @NotNull + @Override + public DiffRequest process(@NotNull ChangeDiffRequestPresentable presentable, + @NotNull UserDataHolder context, + @NotNull ProgressIndicator indicator) throws DiffRequestPresentableException, ProcessCanceledException { + DiffRequestPresentableException e1 = null; + DiffRequestPresentableException e2 = null; + + DiffRequest propertyRequest; + try { + propertyRequest = createPropertyRequest(presentable.getChange(), indicator); + } + catch (DiffRequestPresentableException e) { + e1 = e; + propertyRequest = new ErrorDiffRequest(presentable, e); + } + + DiffRequest contentRequest; + try { + contentRequest = ChangeDiffRequestPresentable.createRequest(presentable.getProject(), presentable.getChange(), context, indicator); + } + catch (DiffRequestPresentableException e) { + e2 = e; + contentRequest = new ErrorDiffRequest(presentable, e); + } + + if (e1 != null && e2 != null) { + LOG.info(e1); + LOG.info(e2); + throw new DiffRequestPresentableException(e1.getMessage() + "\n\n" + e2.getMessage()); + } + + contentRequest.putUserData(DiffViewerWrapper.KEY, new SvnDiffViewerWrapper(propertyRequest)); + + return contentRequest; + } + + @NotNull + private static SvnPropertiesDiffRequest createPropertyRequest(@NotNull Change change, @NotNull ProgressIndicator indicator) + throws DiffRequestPresentableException { + try { + Change propertiesChange = getSvnChangeLayer(change); + if (propertiesChange == null) throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes")); + + ContentRevision bRevRaw = propertiesChange.getBeforeRevision(); + ContentRevision aRevRaw = propertiesChange.getAfterRevision(); + + if (bRevRaw != null && !(bRevRaw instanceof PropertyRevision)) { + LOG.warn("Before change is not PropertyRevision"); + throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes")); + } + if (aRevRaw != null && !(aRevRaw instanceof PropertyRevision)) { + LOG.warn("After change is not PropertyRevision"); + throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes")); + } + + PropertyRevision bRev = (PropertyRevision)bRevRaw; + PropertyRevision aRev = (PropertyRevision)aRevRaw; + + indicator.checkCanceled(); + List bContent = bRev != null ? bRev.getProperties() : null; + + indicator.checkCanceled(); + List aContent = aRev != null ? aRev.getProperties() : null; + + if (aRev == null && bRev == null) throw new DiffRequestPresentableException(SvnBundle.getString("diff.cant.get.properties.changes")); + + ContentRevision bRevMain = change.getBeforeRevision(); + ContentRevision aRevMain = change.getAfterRevision(); + String title1 = bRevMain != null ? StringUtil.nullize(bRevMain.getRevisionNumber().asString()) : null; + String title2 = aRevMain != null ? StringUtil.nullize(aRevMain.getRevisionNumber().asString()) : null; + + return new SvnPropertiesDiffRequest(bContent, aContent, title1, title2); + } + catch (VcsException e) { + throw new DiffRequestPresentableException(e); + } + } + + @Nullable + private static Change getSvnChangeLayer(@NotNull Change change) { + for (Map.Entry entry : change.getOtherLayers().entrySet()) { + if (SvnChangeProvider.PROPERTY_LAYER.equals(entry.getKey())) { + if (change.getOtherLayers().size() != 1) LOG.warn("Some of change layers ignored"); + return entry.getValue(); + } + } + return null; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffSettingsHolder.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffSettingsHolder.java new file mode 100644 index 000000000000..f5e64fb5d7fa --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffSettingsHolder.java @@ -0,0 +1,80 @@ +package org.jetbrains.idea.svn.difftool; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.diff.util.DiffUtil; +import org.jetbrains.annotations.NotNull; + +@State( + name = "SvnDiffSettings", + storages = {@Storage( + file = DiffUtil.DIFF_CONFIG)}) +public class SvnDiffSettingsHolder implements PersistentStateComponent { + public static class SvnDiffSettings { + public static final Key KEY = Key.create("SvnDiffSettings"); + + float SPITTER_PROPORTION = 0.9f; + boolean HIDE_PROPERTIES = false; + + public SvnDiffSettings() { + } + + public SvnDiffSettings(float SPITTER_PROPORTION, boolean HIDE_PROPERTIES) { + this.SPITTER_PROPORTION = SPITTER_PROPORTION; + this.HIDE_PROPERTIES = HIDE_PROPERTIES; + } + + @NotNull + private SvnDiffSettings copy() { + return new SvnDiffSettings(SPITTER_PROPORTION, HIDE_PROPERTIES); + } + + public boolean isHideProperties() { + return HIDE_PROPERTIES; + } + + public void setHideProperties(boolean value) { + HIDE_PROPERTIES = value; + } + + public float getSplitterProportion() { + return SPITTER_PROPORTION; + } + + public void setSplitterProportion(float value) { + SPITTER_PROPORTION = value; + } + + // + // Impl + // + + @NotNull + public static SvnDiffSettings getSettings() { + return getInstance().getState().copy(); + } + + @NotNull + public static SvnDiffSettings getSettingsDefaults() { + return getInstance().getState(); + } + } + + private SvnDiffSettings myState = new SvnDiffSettings(); + + @NotNull + public SvnDiffSettings getState() { + return myState; + } + + public void loadState(SvnDiffSettings state) { + myState = state; + } + + public static SvnDiffSettingsHolder getInstance() { + return ServiceManager.getService(SvnDiffSettingsHolder.class); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffViewer.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffViewer.java new file mode 100644 index 000000000000..a0432287e252 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffViewer.java @@ -0,0 +1,335 @@ +package org.jetbrains.idea.svn.difftool; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext; +import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffViewer; +import com.intellij.openapi.util.diff.api.FrameDiffTool.ToolbarComponents; +import com.intellij.openapi.util.diff.contents.DiffContent; +import com.intellij.openapi.util.diff.contents.EmptyContent; +import com.intellij.openapi.util.diff.impl.ModifiablePanel; +import com.intellij.openapi.util.diff.requests.DiffRequest; +import com.intellij.openapi.util.diff.requests.ErrorDiffRequest; +import com.intellij.openapi.util.diff.tools.ErrorDiffTool; +import com.intellij.openapi.util.diff.util.DiffUtil; +import com.intellij.ui.EditorNotificationPanel; +import com.intellij.ui.OnePixelSplitter; +import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.difftool.SvnDiffSettingsHolder.SvnDiffSettings; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer; +import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class SvnDiffViewer implements DiffViewer { + private static final Logger LOG = Logger.getInstance(SvnDiffViewer.class); + + public static final Key PROPERTY_CONTEXT_KEY = Key.create("MyPropertyContext"); + public static final Key FOCUSED_VIEWER_KEY = Key.create("SvnFocusedViewer"); + + @Nullable private final Project myProject; + + @NotNull private final DiffContext myContext; + @NotNull private final DiffRequest myPropertyRequest; + + @NotNull private final SvnDiffSettings mySettings; + + @NotNull private final JPanel myPanel; + @NotNull private final Splitter mySplitter; + @NotNull private final ModifiablePanel myNotificationPanel; + + @NotNull private final DiffViewer myContentViewer; + @NotNull private final DiffViewer myPropertiesViewer; + + @NotNull private final FocusListener myContentFocusListener = new MyFocusListener(false); + @NotNull private final FocusListener myPropertiesFocusListener = new MyFocusListener(true); + + private boolean myPropertiesViewerFocused; // False - content viewer, True - properties + private boolean myDumbContentViewer; + + public SvnDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest propertyRequest, @NotNull DiffViewer wrappingViewer) { + myProject = context.getProject(); + myContext = context; + myPropertyRequest = propertyRequest; + myContentViewer = wrappingViewer; + + myPropertyRequest.onAssigned(true); + + mySettings = initSettings(context); + + mySplitter = new OnePixelSplitter(true); + mySplitter.setProportion(mySettings.getSplitterProportion()); + mySplitter.setFirstComponent(myContentViewer.getComponent()); + + myNotificationPanel = new ModifiablePanel(); + + MyPropertyContext propertyContext = initPropertyContext(context); + myPropertiesViewer = createPropertiesViewer(propertyRequest, propertyContext); + + myPanel = new JPanel(new BorderLayout()); + myPanel.add(mySplitter, BorderLayout.CENTER); + myPanel.add(myNotificationPanel, BorderLayout.SOUTH); + + updatePropertiesPanel(); + } + + @NotNull + private static DiffViewer createPropertiesViewer(@NotNull DiffRequest propertyRequest, @NotNull MyPropertyContext propertyContext) { + if (propertyRequest instanceof SvnPropertiesDiffRequest) { + return new SvnPropertiesDiffViewer(propertyContext, ((SvnPropertiesDiffRequest)propertyRequest)); + } + else { + return ErrorDiffTool.INSTANCE.createComponent(propertyContext, propertyRequest); + } + } + + @NotNull + @Override + public ToolbarComponents init() { + installListeners(); + + processContextHints(); + + ToolbarComponents properties = myPropertiesViewer.init(); + ToolbarComponents components = new ToolbarComponents(); + components.toolbarActions = createToolbar(properties.toolbarActions); + return components; + } + + @Override + public void dispose() { + destroyListeners(); + + updateContextHints(); + + Disposer.dispose(myPropertiesViewer); + + myPropertyRequest.onAssigned(false); + } + + private void processContextHints() { + if (myContext.getUserData(FOCUSED_VIEWER_KEY) == Boolean.TRUE) myPropertiesViewerFocused = true; + myDumbContentViewer = myContentViewer.getPreferredFocusedComponent() == null; + } + + private void updateContextHints() { + if (!myDumbContentViewer) myContext.putUserData(FOCUSED_VIEWER_KEY, myPropertiesViewerFocused); + mySettings.setSplitterProportion(mySplitter.getProportion()); + } + + // + // Diff + // + + @NotNull + private JComponent createNotification() { + if (myPropertyRequest instanceof ErrorDiffRequest) { + return createNotification(((ErrorDiffRequest)myPropertyRequest).getErrorMessage()); + } + + DiffContent[] contents = ((SvnPropertiesDiffRequest)myPropertyRequest).getContents(); + + Map before = getProperties(contents[0]); + Map after = getProperties(contents[1]); + + if (before.isEmpty() && after.isEmpty()) return createNotification("No SVN properties set"); + + if (!before.keySet().equals(after.keySet())) { + return createNotification("SVN Properties changed"); + } + + for (String key : before.keySet()) { + if (!Comparing.equal(before.get(key), after.get(key))) return createNotification("SVN Properties changed"); + } + + return createNotification("SVN Properties unchanged"); + } + + @NotNull + private static Map getProperties(@NotNull DiffContent content) { + if (content instanceof EmptyContent) return Collections.emptyMap(); + + List properties = ((SvnPropertiesDiffRequest.PropertyContent)content).getProperties(); + + Map map = new HashMap(); + + for (PropertyData data : properties) { + if (map.containsKey(data.getName())) LOG.warn("Duplicated property: " + data.getName()); + map.put(data.getName(), data.getValue()); + } + + return map; + } + + @NotNull + private static JPanel createNotification(@NotNull String text) { + return new EditorNotificationPanel().text(text); + } + + // + // Misc + // + + private void updatePropertiesPanel() { + if (!mySettings.isHideProperties()) { + mySplitter.setSecondComponent(myPropertiesViewer.getComponent()); + myNotificationPanel.setContent(null); + } + else { + mySplitter.setSecondComponent(null); + myNotificationPanel.setContent(createNotification()); + } + } + + @NotNull + private List createToolbar(@Nullable List propertiesActions) { + List result = new ArrayList(); + + if (propertiesActions != null) result.addAll(propertiesActions); + + result.add(new ToggleHidePropertiesAction()); + + return result; + } + + @NotNull + private static SvnDiffSettings initSettings(@NotNull DiffContext context) { + SvnDiffSettings settings = context.getUserData(SvnDiffSettings.KEY); + if (settings == null) { + settings = SvnDiffSettings.getSettingsDefaults(); // TODO: do not use defaults ? + context.putUserData(SvnDiffSettings.KEY, settings); + } + return settings; + } + + @NotNull + private MyPropertyContext initPropertyContext(@NotNull DiffContext context) { + MyPropertyContext propertyContext = context.getUserData(PROPERTY_CONTEXT_KEY); + if (propertyContext == null) { + propertyContext = new MyPropertyContext(); + context.putUserData(PROPERTY_CONTEXT_KEY, propertyContext); + } + return propertyContext; + } + + private void installListeners() { + myContentViewer.getComponent().addFocusListener(myContentFocusListener); + myPropertiesViewer.getComponent().addFocusListener(myPropertiesFocusListener); + } + + private void destroyListeners() { + myContentViewer.getComponent().removeFocusListener(myContentFocusListener); + myPropertiesViewer.getComponent().removeFocusListener(myPropertiesFocusListener); + } + + // + // Getters + // + + @NotNull + @Override + public JComponent getComponent() { + return myPanel; + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + if (myPropertiesViewerFocused) { + JComponent component = myPropertiesViewer.getPreferredFocusedComponent(); + if (component != null) return component; + return myContentViewer.getPreferredFocusedComponent(); + } + else { + JComponent component = myContentViewer.getPreferredFocusedComponent(); + if (component != null) return component; + return myPropertiesViewer.getPreferredFocusedComponent(); + } + } + + // + // Actions + // + + private class ToggleHidePropertiesAction extends ToggleAction implements DumbAware { // TODO: shortcut + public ToggleHidePropertiesAction() { + super("Show SVN properties", null, AllIcons.Actions.Preview); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return !mySettings.isHideProperties(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + mySettings.setHideProperties(!state); + updatePropertiesPanel(); + } + } + + // + // Helpers + // + + private class MyPropertyContext extends UserDataHolderBase implements DiffContext { + private final DiffWindow myWindow = new DiffWindow() { + @Override + public boolean isFocused() { + return DiffUtil.isFocusedComponent(getProject(), myPropertiesViewer.getComponent()); + } + + @Override + public void requestFocus() { + DiffUtil.requestFocus(getProject(), myPropertiesViewer.getPreferredFocusedComponent()); + } + }; + + @Nullable + @Override + public Project getProject() { + return myContext.getProject(); + } + + @NotNull + @Override + public DiffWindow getDiffWindow() { + return myWindow; + } + } + + private class MyFocusListener extends FocusAdapter { + private final boolean myValue; + + public MyFocusListener(boolean value) { + myValue = value; + } + + @Override + public void focusGained(FocusEvent e) { + myPropertiesViewerFocused = myValue; + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffViewerWrapper.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffViewerWrapper.java new file mode 100644 index 000000000000..e5c2b667834c --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/SvnDiffViewerWrapper.java @@ -0,0 +1,20 @@ +package org.jetbrains.idea.svn.difftool; + +import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext; +import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffViewer; +import com.intellij.openapi.util.diff.impl.DiffViewerWrapper; +import com.intellij.openapi.util.diff.requests.DiffRequest; +import org.jetbrains.annotations.NotNull; + +public class SvnDiffViewerWrapper implements DiffViewerWrapper { + @NotNull private final DiffRequest myPropertyRequest; + + public SvnDiffViewerWrapper(@NotNull DiffRequest propertyRequest) { + myPropertyRequest = propertyRequest; + } + + @Override + public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request, @NotNull DiffViewer wrappedViewer) { + return new SvnDiffViewer(context, myPropertyRequest, wrappedViewer); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/PropertiesTableModel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/PropertiesTableModel.java new file mode 100644 index 000000000000..3471b54efd21 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/PropertiesTableModel.java @@ -0,0 +1,254 @@ +package org.jetbrains.idea.svn.difftool.properties; + +import com.intellij.openapi.util.diff.util.Side; +import com.intellij.openapi.util.diff.util.TextDiffType; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.JBColor; +import com.intellij.util.ui.ColumnInfo; +import com.intellij.util.ui.ListTableModel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer.PropertyDiffRecord; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer.PropertyDiffRecord.ColoredChunk; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +// FIXME: colors on selection +public class PropertiesTableModel extends ListTableModel { + @NotNull private final MultiLineTableRenderer myRenderer = new MultiLineTableRenderer(); + + public static int NAME_COL = 0; + + public PropertiesTableModel(boolean showLeft, boolean showRight, @Nullable String title1, @Nullable String title2) { + if (showLeft && showRight) { + setColumnInfos(new ColumnInfo[]{ + new NameColumnInfo(myRenderer), + new ValueColumnInfo(Side.LEFT, title1, myRenderer), + new ValueColumnInfo(Side.RIGHT, title2, myRenderer)}); + } + else if (showLeft) { + setColumnInfos(new ColumnInfo[]{ + new NameColumnInfo(myRenderer), + new ValueColumnInfo(Side.LEFT, title1, myRenderer)}); + } + else if (showRight) { + setColumnInfos(new ColumnInfo[]{ + new NameColumnInfo(myRenderer), + new ValueColumnInfo(Side.RIGHT, title2, myRenderer)}); + } + else { + throw new IllegalStateException(); + } + } + + @Override + public RowSorter.SortKey getDefaultSortKey() { + return new RowSorter.SortKey(NAME_COL, SortOrder.DESCENDING); + } + + private static TextDiffType getDiffType(PropertyDiffRecord record) { + if (record.isChanged()) { + if (record.getBefore() == null) { + return TextDiffType.INSERTED; + } + else if (record.getAfter() == null) { + return TextDiffType.DELETED; + } + else { + return TextDiffType.MODIFIED; + } + } + return null; + } + + private static String getValueTitle(@NotNull Side side, @Nullable String title) { + if (title != null) return "Value in " + title; + return side.isLeft() ? "Value Before" : "Value After"; + } + + private static class NameColumnInfo extends ColumnInfo { + @NotNull private final MultiLineTableRenderer myRenderer; + + public NameColumnInfo(@NotNull MultiLineTableRenderer renderer) { + super("SVN Property Name"); + myRenderer = renderer; + } + + @Nullable + @Override + public TableCellRenderer getRenderer(final PropertyDiffRecord record) { + TextDiffType type = getDiffType(record); + final Color bgColor = type != null ? type.getIgnoredColor(null) : null; + myRenderer.setCustomizer(new MultiLineTableRenderer.Customizer() { + @Override + public void customize(JTable table, JLabel label, boolean isSelected, Object value) { + label.setText(record.getName()); + if (bgColor != null) { + label.setForeground(table.getForeground()); + label.setBackground(bgColor); + } + } + }); + return myRenderer; + } + + @Nullable + @Override + public String valueOf(PropertyDiffRecord data) { + return data.getName(); + } + + @Nullable + @Override + public String getPreferredStringValue() { + return "svn:some_property_name"; + } + + + @Nullable + @Override + public String getMaxStringValue() { + return "svn:some_really_big_cool_property_name"; + } + + @Nullable + @Override + public Comparator getComparator() { + return new Comparator() { + @Override + public int compare(PropertyDiffRecord o1, PropertyDiffRecord o2) { + return StringUtil.naturalCompare(o1.getName(), o2.getName()); + } + }; + } + } + + private static class ValueColumnInfo extends ColumnInfo { + @NotNull private final MultiLineTableRenderer myRenderer; + @NotNull private final Side mySide; + + public ValueColumnInfo(@NotNull Side side, @Nullable String title, @NotNull MultiLineTableRenderer renderer) { + super(getValueTitle(side, title)); + mySide = side; + myRenderer = renderer; + } + + @Nullable + private List getChunks(@NotNull PropertyDiffRecord record) { + return mySide.isLeft() ? record.getBefore() : record.getAfter(); + } + + @Nullable + @Override + public TableCellRenderer getRenderer(final PropertyDiffRecord record) { + TextDiffType type = getDiffType(record); + final Color bgColor = type != null ? type.getIgnoredColor(null) : null; + myRenderer.setCustomizer(new MultiLineTableRenderer.Customizer() { + @Override + public void customize(JTable table, JLabel label, boolean isSelected, Object value) { + List chunks = getChunks(record); + if (chunks == null) { + label.setText(""); + label.setBackground(JBColor.border()); // TODO: better color + } + else { + StringBuilder text = new StringBuilder(); + text.append(""); + for (ColoredChunk chunk : chunks) { + text.append("'); + text.append(StringUtil.escapeXml(chunk.getText()).replaceAll("\n", "
")); + text.append(""); + } + text.append(""); + label.setText(text.toString()); + if (bgColor != null) { + label.setForeground(table.getForeground()); + label.setBackground(bgColor); + } + } + } + }); + return myRenderer; + } + + @Nullable + @Override + public String valueOf(@NotNull PropertyDiffRecord record) { + List chunks = getChunks(record); + if (chunks == null) return null; + + StringBuilder builder = new StringBuilder(); + for (ColoredChunk chunk : chunks) { + builder.append(chunk.getText()); + } + return builder.toString(); + } + } + + private static class MultiLineTableRenderer extends DefaultTableCellRenderer { + @NotNull private final List> rowColHeight = new ArrayList>(); + @Nullable private Customizer myCustomizer; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { + setBackground(null); + final JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + if (myCustomizer != null) myCustomizer.customize(table, label, isSelected, value); + + adjustRowHeight(table, row, column); + + return label; + } + + /** + * Calculate the new preferred height for a given row, and sets the height on the table. + */ + private void adjustRowHeight(JTable table, int row, int column) { + //The trick to get this to work properly is to set the width of the column to the + //textarea. The reason for this is that getPreferredSize(), without a width tries + //to place all the text in one line. By setting the size with the with of the column, + //getPreferredSize() returnes the proper height which the row should have in + //order to make room for the text. + int prefH = getPreferredSize().height; + while (rowColHeight.size() <= row) { + rowColHeight.add(new ArrayList(column)); + } + List colHeights = rowColHeight.get(row); + while (colHeights.size() <= column) { + colHeights.add(0); + } + colHeights.set(column, prefH); + int maxH = prefH; + for (Integer colHeight : colHeights) { + if (colHeight > maxH) { + maxH = colHeight; + } + } + if (table.getRowHeight(row) != maxH) { + table.setRowHeight(row, maxH); + } + } + + public void setCustomizer(@Nullable Customizer customizer) { + myCustomizer = customizer; + } + + public interface Customizer { + void customize(JTable table, JLabel textArea, boolean isSelected, Object value); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffRequest.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffRequest.java new file mode 100644 index 000000000000..1bd9af62e022 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffRequest.java @@ -0,0 +1,100 @@ +package org.jetbrains.idea.svn.difftool.properties; + +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.util.diff.contents.DiffContent; +import com.intellij.openapi.util.diff.contents.EmptyContent; +import com.intellij.openapi.util.diff.requests.ContentDiffRequest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.properties.PropertyData; + +import java.util.List; + +public class SvnPropertiesDiffRequest extends UserDataHolderBase implements ContentDiffRequest { + @NotNull private final DiffContent[] myContents; + @NotNull private final String[] myContentTitles; + @NotNull private final String myWindowTitle; + + public SvnPropertiesDiffRequest(@NotNull String windowTitle, + @NotNull DiffContent content1, + @NotNull DiffContent content2, + @NotNull String title1, + @NotNull String title2) { + myWindowTitle = windowTitle; + myContents = new DiffContent[]{content1, content2}; + myContentTitles = new String[]{title1, title2}; + + assert content1 instanceof PropertyContent || content1 instanceof EmptyContent; + assert content2 instanceof PropertyContent || content2 instanceof EmptyContent; + assert content1 instanceof PropertyContent || content2 instanceof PropertyContent; + } + + public SvnPropertiesDiffRequest(@Nullable List before, @Nullable List after, + @Nullable String title1, @Nullable String title2) { + assert before != null || after != null; + + myContents = new DiffContent[]{createContent(before), createContent(after)}; + myWindowTitle = "Svn Properties Diff"; + myContentTitles = new String[]{title1, title2}; + } + + @NotNull + public DiffContent createContent(@Nullable List content) { + if (content == null) return new EmptyContent(); + + return new PropertyContent(content); + } + + @NotNull + @Override + public String getWindowTitle() { + return myWindowTitle; + } + + @NotNull + @Override + public String[] getContentTitles() { + return myContentTitles; + } + + @NotNull + @Override + public DiffContent[] getContents() { + return myContents; + } + + @Override + public void onAssigned(boolean isAssigned) { + } + + public static class PropertyContent implements DiffContent { + @NotNull private final List myProperties; + + public PropertyContent(@NotNull List properties) { + myProperties = properties; + } + + @NotNull + public List getProperties() { + return myProperties; + } + + @Nullable + @Override + public FileType getContentType() { + return null; + } + + @Nullable + @Override + public OpenFileDescriptor getOpenFileDescriptor() { + return null; + } + + @Override + public void onAssigned(boolean isAssigned) { + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffTool.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffTool.java new file mode 100644 index 000000000000..71a7299acc32 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffTool.java @@ -0,0 +1,24 @@ +package org.jetbrains.idea.svn.difftool.properties; + +import com.intellij.openapi.util.diff.api.FrameDiffTool; +import com.intellij.openapi.util.diff.requests.DiffRequest; +import org.jetbrains.annotations.NotNull; + +public class SvnPropertiesDiffTool implements FrameDiffTool { + @NotNull + @Override + public String getName() { + return "Svn Properties Viewer"; + } + + @Override + public boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) { + return request instanceof SvnPropertiesDiffRequest; + } + + @NotNull + @Override + public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request) { + return new SvnPropertiesDiffViewer(context, (SvnPropertiesDiffRequest)request); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffViewer.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffViewer.java new file mode 100644 index 000000000000..f2996e261d12 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffViewer.java @@ -0,0 +1,237 @@ +package org.jetbrains.idea.svn.difftool.properties; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext; +import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffViewer; +import com.intellij.openapi.util.diff.api.FrameDiffTool.ToolbarComponents; +import com.intellij.openapi.util.diff.comparison.iterables.DiffIterable; +import com.intellij.openapi.util.diff.comparison.iterables.DiffIterableUtil; +import com.intellij.openapi.util.diff.contents.DiffContent; +import com.intellij.openapi.util.diff.fragments.DiffFragment; +import com.intellij.openapi.util.diff.util.DiffUtil; +import com.intellij.openapi.util.diff.util.TextDiffType; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.table.TableView; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffViewer.PropertyDiffRecord.ColoredChunk; +import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; + +import javax.swing.*; +import java.awt.*; +import java.util.*; +import java.util.List; + +public class SvnPropertiesDiffViewer implements DiffViewer { + private static final Logger LOG = Logger.getInstance(SvnPropertiesDiffViewer.class); + + @Nullable private final Project myProject; + + @NotNull private final DiffContext myContext; + @NotNull private final SvnPropertiesDiffRequest myRequest; + + @Nullable private final List myProperties1; + @Nullable private final List myProperties2; + + @NotNull private final JPanel myPanel; + @NotNull private final TableView myTable; + + public SvnPropertiesDiffViewer(@NotNull DiffContext context, @NotNull SvnPropertiesDiffRequest request) { + myProject = context.getProject(); + + myContext = context; + myRequest = request; + + String[] titles = request.getContentTitles(); + + DiffContent[] contents = request.getContents(); + myProperties1 = getProperties(contents[0]); + myProperties2 = getProperties(contents[1]); + assert myProperties1 != null || myProperties2 != null; + + PropertiesTableModel model = new PropertiesTableModel(myProperties1 != null, myProperties2 != null, titles[0], titles[1]); + myTable = new TableView(model); + myTable.getTableHeader().setReorderingAllowed(false); + + myPanel = new JPanel(new BorderLayout()); + myPanel.add(ScrollPaneFactory.createScrollPane(myTable), BorderLayout.CENTER); + } + + @NotNull + @Override + public ToolbarComponents init() { + rediff(); + + ToolbarComponents components = new ToolbarComponents(); + components.toolbarActions = createToolbar(); + return components; + } + + @Override + public void dispose() { + } + + // + // Diff + // + + public void rediff() { + assert myProperties1 != null || myProperties2 != null; + + Map before = new HashMap(); + Map after = new HashMap(); + if (myProperties1 != null) { + for (PropertyData data : myProperties1) { + before.put(data.getName(), data.getValue()); + } + } + if (myProperties2 != null) { + for (PropertyData data : myProperties2) { + after.put(data.getName(), data.getValue()); + } + } + + List records = new ArrayList(); + for (String name : ContainerUtil.union(before.keySet(), after.keySet())) { + records.add(createRecord(name, before.get(name), after.get(name))); + } + ContainerUtil.sort(records, new Comparator() { + @Override + public int compare(PropertyDiffRecord o1, PropertyDiffRecord o2) { + return StringUtil.naturalCompare(o1.getName(), o2.getName()); + } + }); + ((PropertiesTableModel)myTable.getModel()).setItems(records); + } + + @Nullable + private static PropertyDiffRecord createRecord(@NotNull String name, @Nullable PropertyValue value1, @Nullable PropertyValue value2) { + assert value1 != null || value2 != null; + if (value1 == null) { + return new PropertyDiffRecord(name, null, Collections.singletonList(new ColoredChunk(value2.toString(), null)), true); + } + if (value2 == null) { + return new PropertyDiffRecord(name, Collections.singletonList(new ColoredChunk(value1.toString(), null)), null, true); + } + + String text1 = value1.toString(); + String text2 = value2.toString(); + + List fragments = DiffUtil.compareWords(text1, text2, 1000); + + List chunks1 = new ArrayList(); + List chunks2 = new ArrayList(); + + DiffIterable iterable = DiffIterableUtil.createFragments(fragments, text1.length(), text2.length()); + for (Pair pair : DiffIterableUtil.iterateAll(iterable)) { + DiffIterableUtil.Range range = pair.first; + Boolean equals = pair.second; + + if (range.start1 == range.end1) { + chunks2.add(new ColoredChunk(text2.substring(range.start2, range.end2), equals ? null : TextDiffType.INSERTED)); + } + else if (range.start2 == range.end2) { + chunks1.add(new ColoredChunk(text1.substring(range.start1, range.end1), equals ? null : TextDiffType.DELETED)); + } + else { + chunks1.add(new ColoredChunk(text1.substring(range.start1, range.end1), equals ? null : TextDiffType.MODIFIED)); + chunks2.add(new ColoredChunk(text2.substring(range.start2, range.end2), equals ? null : TextDiffType.MODIFIED)); + } + } + + return new PropertyDiffRecord(name, chunks1, chunks2, !fragments.isEmpty()); + } + + @Nullable + private static List getProperties(@NotNull DiffContent content) { + if (content instanceof SvnPropertiesDiffRequest.PropertyContent) { + return ((SvnPropertiesDiffRequest.PropertyContent)content).getProperties(); + } + return null; + } + + @NotNull + private List createToolbar() { + List result = new ArrayList(); + return result; + } + + // + // Getters + // + + @NotNull + @Override + public JComponent getComponent() { + return myPanel; + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myTable; + } + + public static class PropertyDiffRecord { + @NotNull private final String myName; + @Nullable private final List myBefore; + @Nullable private final List myAfter; + private final boolean myIsChanged; + + public PropertyDiffRecord(@NotNull String name, + @Nullable List before, + @Nullable List after, + boolean isChanged) { + myName = name; + myBefore = before; + myAfter = after; + myIsChanged = isChanged; + } + + @NotNull + public String getName() { + return myName; + } + + @Nullable + public List getBefore() { + return myBefore; + } + + @Nullable + public List getAfter() { + return myAfter; + } + + public boolean isChanged() { + return myIsChanged; + } + + public static class ColoredChunk { + @NotNull private final String myText; + @Nullable private final TextDiffType myType; + + public ColoredChunk(@NotNull String text, @Nullable TextDiffType type) { + myText = text; + myType = type; + } + + @NotNull + public String getText() { + return myText; + } + + @Nullable + public TextDiffType getType() { + return myType; + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/PropertyRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/PropertyRevision.java new file mode 100644 index 000000000000..3fcdc3619885 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/PropertyRevision.java @@ -0,0 +1,13 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.ContentRevision; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.properties.PropertyData; + +import java.util.List; + +public interface PropertyRevision extends ContentRevision { + @Nullable + List getProperties() throws VcsException; +} \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SimplePropertyRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SimplePropertyRevision.java new file mode 100644 index 000000000000..881984cb1fb6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SimplePropertyRevision.java @@ -0,0 +1,53 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.ContentRevision; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction; +import org.jetbrains.idea.svn.properties.PropertyData; + +import java.util.List; + +public class SimplePropertyRevision implements ContentRevision, PropertyRevision { + private final List myProperty; + private final FilePath myNewFilePath; + private final String myRevision; + + @Nullable + @Override + public List getProperties() throws VcsException { + return myProperty; + } + + public SimplePropertyRevision(final List property, final FilePath newFilePath, final String revision) { + myProperty = property; + myNewFilePath = newFilePath; + myRevision = revision; + } + + @Nullable + public String getContent() { + return AbstractShowPropertiesDiffAction.toSortedStringPresentation(myProperty); + } + + @NotNull + public FilePath getFile() { + return myNewFilePath; + } + + @NotNull + public VcsRevisionNumber getRevisionNumber() { + return new VcsRevisionNumber() { + public String asString() { + return myRevision; + } + + public int compareTo(final VcsRevisionNumber o) { + return 0; + } + }; + } +} \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLazyPropertyContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLazyPropertyContentRevision.java index c5c3ab3d3058..5499a2d8ca21 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLazyPropertyContentRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLazyPropertyContentRevision.java @@ -26,24 +26,28 @@ import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.MarkerVcsContentRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnRevisionNumber; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.SVNURL; +import java.util.List; + /** * Created with IntelliJ IDEA. * User: Irina.Chernushina * Date: 2/22/12 * Time: 10:28 AM */ -public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVcsContentRevision { +public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVcsContentRevision, PropertyRevision { private final FilePath myPath; private final VcsRevisionNumber myNumber; private final Project myProject; private final SVNURL myUrl; - private String myContent; + private List myContent; public SvnLazyPropertyContentRevision(FilePath path, VcsRevisionNumber number, Project project, SVNURL url) { myPath = path; @@ -52,17 +56,24 @@ public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVc myUrl = url; } + @Nullable @Override - public String getContent() throws VcsException { + public List getProperties() throws VcsException { if (myContent == null) { myContent = loadContent(); } return myContent; } - private String loadContent() { + @Override + public String getContent() throws VcsException { + return AbstractShowPropertiesDiffAction.toSortedStringPresentation(getProperties()); + } + + private List loadContent() throws VcsException { final SvnVcs vcs = SvnVcs.getInstance(myProject); - final Ref ref = new Ref(); + final Ref> ref = new Ref>(); + final Ref exceptionRef = new Ref(); final Runnable runnable = new Runnable() { @Override public void run() { @@ -70,24 +81,21 @@ public class SvnLazyPropertyContentRevision implements ContentRevision, MarkerVc ref.set(AbstractShowPropertiesDiffAction.getPropertyList(vcs, myUrl, ((SvnRevisionNumber)myNumber).getRevision())); } catch (VcsException e) { - // unknown node kind (node deleted) - /*if (e.getErrorMessage().getErrorCode().getCode() == 145000) { - return ""; - }*/ - ref.set("Can not get properties: " + e.getMessage()); + exceptionRef.set(e); } } }; if (ApplicationManager.getApplication().isDispatchThread()) { final boolean completed = ProgressManager.getInstance() .runProcessWithProgressSynchronously(runnable, SvnBundle.message("progress.title.loading.file.properties"), true, myProject); - if (! completed) { - return "Properties load for revision " + getRevisionNumber().asString() + " was canceled."; + if (!completed) { + throw new VcsException("Properties load for revision " + getRevisionNumber().asString() + " was canceled."); } } else { runnable.run(); } + if (!exceptionRef.isNull()) throw exceptionRef.get(); return ref.get(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java index 52e5438997ee..ce017be09c2f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java @@ -52,4 +52,21 @@ public class PropertyValue { public String toString() { return myValue; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PropertyValue value = (PropertyValue)o; + + if (!myValue.equals(value.myValue)) return false; + + return true; + } + + @Override + public int hashCode() { + return myValue.hashCode(); + } } diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml index f88970ebf676..ce9f4884e63e 100644 --- a/plugins/svn4idea/svn4idea.iml +++ b/plugins/svn4idea/svn4idea.iml @@ -92,6 +92,6 @@ + - - + \ No newline at end of file