push dialog: mercurial implementation

* api created in dvcs-api module;
* pushLog class implements complex tree ui with editor and renderer;
* push controller created a push repo-spec model and managed actions between ui and model
This commit is contained in:
Nadya Zabrodina
2014-08-11 02:28:44 +04:00
parent d544d499ba
commit dcd1536005
111 changed files with 2848 additions and 122 deletions
+2 -1
View File
@@ -47,7 +47,8 @@
<module fileurl="file://$PROJECT_DIR$/xml/dom-openapi/dom-openapi.iml" filepath="$PROJECT_DIR$/xml/dom-openapi/dom-openapi.iml" group="xml" />
<module fileurl="file://$PROJECT_DIR$/xml/dom-tests/dom-tests.iml" filepath="$PROJECT_DIR$/xml/dom-tests/dom-tests.iml" group="xml" />
<module fileurl="file://$PROJECT_DIR$/platform/duplicates-analysis/duplicates-analysis.iml" filepath="$PROJECT_DIR$/platform/duplicates-analysis/duplicates-analysis.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/dvcs/dvcs.iml" filepath="$PROJECT_DIR$/platform/dvcs/dvcs.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/dvcs-impl/dvcs-impl.iml" filepath="$PROJECT_DIR$/platform/dvcs-impl/dvcs-impl.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/platform/dvcs-api/dvcs-api.iml" filepath="$PROJECT_DIR$/platform/dvcs-api/dvcs-api.iml" group="platform"/>
<module fileurl="file://$PROJECT_DIR$/plugins/eclipse/eclipse.iml" filepath="$PROJECT_DIR$/plugins/eclipse/eclipse.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/plugins/eclipse/jps-plugin/eclipse-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/eclipse/jps-plugin/eclipse-jps-plugin.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/platform/editor-ui-api/editor-ui-api.iml" filepath="$PROJECT_DIR$/platform/editor-ui-api/editor-ui-api.iml" group="platform" />
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="util-rt" />
<orderEntry type="module" module-name="vcs-log-graph-api" exported="" />
<orderEntry type="module" module-name="core-api" />
<orderEntry type="module" module-name="vcs-api" />
<orderEntry type="module" module-name="extensions" />
<orderEntry type="module" module-name="vcs-log-api" />
</component>
</module>
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import com.intellij.dvcs.repo.Repository;
import org.jetbrains.annotations.NotNull;
/**
* Provider for outgoing commits
*/
public abstract class OutgoingCommitsProvider {
/**
* Collect outgoing commits or errors for selected repo for specified {@link PushSpec} and store to {@link OutgoingResult}
*
* @param initial true for first commits loading, which identify that all inside actions should be silent
* and do not ask user about smth, a.e authorization request
*/
@NotNull
public abstract OutgoingResult getOutgoingCommits(@NotNull Repository repository,
@NotNull PushSpec pushSpec, boolean initial);
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import com.intellij.vcs.log.VcsFullCommitDetails;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class OutgoingResult {
@NotNull private final List<VcsError> myErrors;
@NotNull private final List<? extends VcsFullCommitDetails> myCommits;
public OutgoingResult(@NotNull List<? extends VcsFullCommitDetails> commits, @NotNull List<VcsError> errors) {
myCommits = commits;
myErrors = errors;
}
@NotNull
public List<VcsError> getErrors() {
return myErrors;
}
@NotNull
public List<? extends VcsFullCommitDetails> getCommits() {
return myCommits;
}
public boolean hasErrors() {
return !myErrors.isEmpty();
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2014 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.dvcs.push;
/**
* Source to push from. For example, local branch for git or branch/bookmark for mercurial.
*/
public interface PushSource {
String getPresentation();
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Specified a push from-to settings for one repository
*/
public class PushSpec {
@NotNull private PushSource mySource;
@Nullable private PushTarget myTarget;
public PushSpec(@NotNull PushSource source, @Nullable PushTarget target) {
mySource = source;
myTarget = target;
}
@NotNull
public PushSource getSource() {
return mySource;
}
@Nullable
public PushTarget getTarget() {
return myTarget;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import com.intellij.dvcs.repo.Repository;
import com.intellij.dvcs.repo.RepositoryManager;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.vcs.AbstractVcs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* Base class to provide vcs-specific info
*/
public abstract class PushSupport {
public static final ExtensionPointName<PushSupport> PUSH_SUPPORT_EP = ExtensionPointName.create("com.intellij.pushSupport");
@NotNull
public abstract AbstractVcs getVcs();
@NotNull
public abstract Pusher getPusher();
@NotNull
public abstract OutgoingCommitsProvider getOutgoingCommitsProvider();
/**
* @return Default push destination
*/
@Nullable
public abstract PushTarget getDefaultTarget(@NotNull Repository repository);
/**
* @return All remembered remote destinations used for completion
*/
@NotNull
public abstract Collection<String> getTargetNames(@NotNull Repository repository);
/**
* @return current source(branch) for repository
*/
@NotNull
public abstract PushSource getSource(@NotNull Repository repository);
/** Create destination target from user input string
* @return
*/
@Nullable
public abstract PushTarget createTarget(String targetName);
/**
* @return RepositoryManager for vcs
*/
@NotNull
public abstract RepositoryManager<? extends Repository> getRepositoryManager();
@Nullable
public VcsPushOptionsPanel getVcsPushOptionsPanel(){
return null;
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import org.jetbrains.annotations.NotNull;
/**
* Destination for push action. (Remote for git or push-path for mercurial).
*/
public interface PushTarget {
@NotNull
String getPresentation();
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import com.intellij.dvcs.repo.Repository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* Base class to execute push command.
*/
public abstract class Pusher {
/**
* Perform push command for all repositories belonged to one vcs.
*
* @param pushSpecs specify push from and to params
* @param vcsPushOptionValue specify additional options to push, null if not supported
* @param force if true then execute force push
*/
public abstract void push(@NotNull Map<Repository, PushSpec> pushSpecs,
@Nullable VcsPushOptionValue vcsPushOptionValue,
boolean force);
}
@@ -0,0 +1,24 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
public interface TreeNodeLinkListener {
void onClick(@NotNull DefaultMutableTreeNode source);
}
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import org.jetbrains.annotations.NotNull;
public class VcsError extends VcsLinkedText {
public VcsError(@NotNull String text) {
super(text, "");
}
public VcsError(@NotNull String text, @NotNull String link) {
super(text, link);
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.List;
public class VcsLinkedText {
@NotNull String myText;
@NotNull String myHandledLink;
private final List<TreeNodeLinkListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
public VcsLinkedText(@NotNull String text, @NotNull String link) {
myText = text;
myHandledLink = link;
}
@NotNull
public String getText() {
return myText;
}
@NotNull
public String getLinkText() {
return myHandledLink;
}
public void addClickListener(@NotNull TreeNodeLinkListener listener) {
myListeners.add(listener);
}
public void fireOnClick(@NotNull DefaultMutableTreeNode relatedNode) {
for (TreeNodeLinkListener listener : myListeners) {
listener.onClick(relatedNode);
}
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2000-2014 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.dvcs.push;
public interface VcsPushOptionValue {
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import javax.swing.*;
import java.awt.event.ActionListener;
public abstract class VcsPushOptionsPanel extends JPanel {
public abstract VcsPushOptionValue getValue();
/**
* @param listener handle valueChange event
*/
public abstract void addValueChangeListener(ActionListener listener);
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2014 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.dvcs.push;
/**
* Specify which references(tags) should be pushed
*/
public enum VcsPushReferenceStrategy {
none, // do not push references/tags
follow, // push references only for selected sources
all // push all references for all repositories
}
@@ -17,6 +17,7 @@ package com.intellij.dvcs.repo;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -97,6 +98,9 @@ public interface Repository extends Disposable {
@NotNull
State getState();
@Nullable
AbstractVcs getVcs();
/**
* Returns the hash of the revision, which HEAD currently points to.
* Returns null only in the case of a fresh repository, when no commit have been made.
@@ -17,6 +17,8 @@
<orderEntry type="module" module-name="vcs-log-api" />
<orderEntry type="module" module-name="vcs-log-impl" />
<orderEntry type="module" module-name="images" />
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="module" module-name="dvcs-api" />
</component>
</module>
+12
View File
@@ -0,0 +1,12 @@
<idea-plugin>
<extensionPoints>
<extensionPoint name="pushSupport"
interface="com.intellij.dvcs.push.PushSupport" area="IDEA_PROJECT"/>
</extensionPoints>
<actions>
<!--<action id="Vcs.Push" class="">-->
<!--<keyboard-shortcut first-keystroke="control shift K" keymap="$default"/>-->
<!--</action>-->
</actions>
</idea-plugin>
@@ -39,6 +39,8 @@ import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.status.StatusBarUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.vcs.log.TimedVcsCommit;
import com.intellij.vcs.log.VcsLog;
import com.intellij.vcs.log.VcsLogProvider;
import org.intellij.images.editor.ImageFileEditor;
@@ -55,6 +57,8 @@ import java.util.List;
*/
public class DvcsUtil {
private static final int SHORT_HASH_LENGTH = 8;
public static void installStatusBarWidget(@NotNull Project project, @NotNull StatusBarWidget widget) {
StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);
if (statusBar != null) {
@@ -183,4 +187,19 @@ public class DvcsUtil {
return result;
}
@NotNull
public static String getShortHash(@NotNull String hash) {
if (hash.length() == 0) return "";
if (hash.length() == 40) return hash.substring(0, SHORT_HASH_LENGTH);
if (hash.length() > 40) // revision string encoded with date too
{
return hash.substring(hash.indexOf("[") + 1, SHORT_HASH_LENGTH);
}
return hash;
}
@NotNull
public static String getDateString(@NotNull TimedVcsCommit commit) {
return DateFormatUtil.formatPrettyDateTime(commit.getTimestamp()) + " ";
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2014 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.dvcs.push;
public class MoreCommitsLink extends VcsLinkedText {
MoreCommitsLink() {
super("", "...");
}
}
@@ -0,0 +1,378 @@
/*
* Copyright 2000-2014 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.dvcs.push;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.dvcs.push.ui.*;
import com.intellij.dvcs.repo.Repository;
import com.intellij.dvcs.repo.RepositoryManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.vcs.log.VcsFullCommitDetails;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
public class PushController implements Disposable {
@NotNull private final Project myProject;
@NotNull private final List<PushSupport> myPushSupports;
@NotNull private final PushLog myPushLog;
private boolean mySingleRepoProject;
private static final int DEFAULT_CHILDREN_PRESENTATION_NUMBER = 20;
private final Map<PushSupport, MyPushOptionValueModel> myAdditionalValuesMap;
private final Map<RepositoryNode, MyRepoModel> myView2Model = new HashMap<RepositoryNode, MyRepoModel>();
public PushController(@NotNull Project project,
@NotNull VcsPushDialog dialog,
@NotNull List<? extends Repository> preselectedRepositories) {
myProject = project;
//todo what would be in case of null
myPushSupports = Arrays.asList(Extensions.getExtensions(PushSupport.PUSH_SUPPORT_EP, myProject));
CheckedTreeNode rootNode = new CheckedTreeNode(null);
mySingleRepoProject = createTreeModel(rootNode, preselectedRepositories);
myPushLog = new PushLog(myProject, rootNode);
myAdditionalValuesMap = new HashMap<PushSupport, MyPushOptionValueModel>();
startLoadingCommits();
Disposer.register(dialog.getDisposable(), this);
}
private void startLoadingCommits() {
Map<RepositoryNode, MyRepoModel> priorityLoading = new HashMap<RepositoryNode, MyRepoModel>();
Map<RepositoryNode, MyRepoModel> others = new HashMap<RepositoryNode, MyRepoModel>();
for (Map.Entry<RepositoryNode, MyRepoModel> entry : myView2Model.entrySet()) {
MyRepoModel model = entry.getValue();
if (model.isSelected()) {
priorityLoading.put(entry.getKey(), model);
}
else {
others.put(entry.getKey(), model);
}
}
loadCommitsFromMap(priorityLoading);
loadCommitsFromMap(others);
}
private void loadCommitsFromMap(@NotNull Map<RepositoryNode, MyRepoModel> items) {
for (Map.Entry<RepositoryNode, MyRepoModel> entry : items.entrySet()) {
RepositoryNode node = entry.getKey();
myPushLog.startLoading(node);
loadCommits(entry.getValue(), node, true);
}
}
//return is single repository project or not
private boolean createTreeModel(@NotNull CheckedTreeNode rootNode, @NotNull List<? extends Repository> preselectedRepositories) {
if (myPushSupports.isEmpty()) return true;
boolean isSingleRepositoryProject = myPushSupports.size() == 1;
for (final PushSupport support : myPushSupports) {
RepositoryManager<? extends Repository> repositoryManager = support.getRepositoryManager();
List<? extends Repository> repositories = repositoryManager.getRepositories();
isSingleRepositoryProject = isSingleRepositoryProject && repositories.size() == 1;
for (Repository repository : repositories) {
PushTarget target = support.getDefaultTarget(repository);
final MyRepoModel model = new MyRepoModel(repository, support, preselectedRepositories.contains(repository),
new PushSpec(support.getSource(repository),
target),
DEFAULT_CHILDREN_PRESENTATION_NUMBER);
RepositoryWithBranchPanel repoPanel = new RepositoryWithBranchPanel(myProject, DvcsUtil.getShortRepositoryName(repository),
support.getSource(repository).getPresentation(),
target == null ? "" : target.getPresentation(),
support.getTargetNames(repository));
final RepositoryNode repoNode =
isSingleRepositoryProject ? new SingleRepositoryNode(repoPanel) : new RepositoryNode(repoPanel);
myView2Model.put(repoNode, model);
repoNode.setChecked(model.isSelected());
repoNode.addRepoNodeListener(new RepositoryNodeListener() {
@Override
public void onTargetChanged(String newValue) {
myView2Model.get(repoNode).setSpec(new PushSpec(model.getSpec().getSource(), support.createTarget(newValue)));
myPushLog.startLoading(repoNode);
loadCommits(model, repoNode, false);
}
@Override
public void onSelectionChanged(boolean isSelected) {
myView2Model.get(repoNode).setSelected(isSelected);
}
});
rootNode.add(repoNode);
}
}
return isSingleRepositoryProject;
}
private void loadCommits(@NotNull final MyRepoModel model,
@NotNull final RepositoryNode node,
final boolean initial) {
node.stopLoading();
final ProgressIndicator indicator = node.startLoading();
final PushSupport support = model.getSupport();
final AtomicReference<OutgoingResult> result = new AtomicReference<OutgoingResult>();
Task.Backgroundable task = new Task.Backgroundable(myProject, "Loading Commits", true) {
@Override
public void onCancel() {
node.stopLoading();
}
@Override
public void onSuccess() {
OutgoingResult outgoing = result.get();
if (outgoing.hasErrors()) {
myPushLog.setChildren(node, ContainerUtil.map(outgoing.getErrors(), new Function<VcsError, DefaultMutableTreeNode>() {
@Override
public DefaultMutableTreeNode fun(VcsError error) {
return new TextWithLinkNode(error);
}
}), model.isSelected());
}
else {
model.setLoadedCommits(outgoing.getCommits());
myPushLog.setChildren(node,
getPresentationForCommits(PushController.this.myProject, model.getLoadedCommits(),
model.getNumberOfShownCommits()), model.isSelected());
}
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
OutgoingResult outgoing = support.getOutgoingCommitsProvider()
.getOutgoingCommits(model.getRepository(), model.getSpec(), initial);
result.compareAndSet(null, outgoing);
}
};
ProgressManagerImpl.runProcessWithProgressAsynchronously(task, indicator, null, ModalityState.any());
}
public PushLog getPushPanelInfo() {
return myPushLog;
}
public boolean isValid() {
//todo implement
return false;
}
public void push(final boolean force) {
Task.Backgroundable task = new Task.Backgroundable(myProject, "Pushing...", false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
for (PushSupport support : myPushSupports) {
MyPushOptionValueModel additionalOptionsModel = myAdditionalValuesMap.get(support);
support.getPusher()
.push(collectPushInfoForVcs(support), additionalOptionsModel == null ? null : additionalOptionsModel.getCurrentValue(), force);
}
}
};
task.queue();
}
@NotNull
private Map<Repository, PushSpec> collectPushInfoForVcs(@NotNull final PushSupport pushSupport) {
Map<Repository, PushSpec> pushSpecs = new HashMap<Repository, PushSpec>();
Collection<MyRepoModel> repositoriesInformation = getSelectedRepoNode();
for (MyRepoModel repoModel : repositoriesInformation) {
if (repoModel.getSupport().equals(pushSupport)) {
pushSpecs.put(repoModel.getRepository(), repoModel.getSpec());
}
}
return pushSpecs;
}
public Collection<MyRepoModel> getSelectedRepoNode() {
if (mySingleRepoProject) {
return myView2Model.values();
}
return ContainerUtil.filter(myView2Model.values(), new Condition<MyRepoModel>() {
@Override
public boolean value(MyRepoModel model) {
return model.isSelected();
}
});
}
@Override
public void dispose() {
for (RepositoryNode node : myView2Model.keySet()) {
node.stopLoading();
}
}
private void addMoreCommits(RepositoryNode repositoryNode) {
MyRepoModel repoModel = myView2Model.get(repositoryNode);
repoModel.increaseShownCommits();
myPushLog.setChildren(repositoryNode,
getPresentationForCommits(
myProject,
repoModel.getLoadedCommits(),
repoModel.getNumberOfShownCommits()
));
}
@NotNull
public List<DefaultMutableTreeNode> getPresentationForCommits(@NotNull final Project project,
@NotNull List<? extends VcsFullCommitDetails> commits,
int commitsNum) {
Function<VcsFullCommitDetails, DefaultMutableTreeNode> commitToNode = new Function<VcsFullCommitDetails, DefaultMutableTreeNode>() {
@Override
public DefaultMutableTreeNode fun(VcsFullCommitDetails commit) {
return new VcsFullCommitDetailsNode(project, commit);
}
};
List<DefaultMutableTreeNode> childrenToShown = new ArrayList<DefaultMutableTreeNode>();
for (int i = 0; i < commits.size(); ++i) {
if (i >= commitsNum) {
final MoreCommitsLink moreCommitsLink = new MoreCommitsLink();
moreCommitsLink.addClickListener(new TreeNodeLinkListener() {
@Override
public void onClick(@NotNull DefaultMutableTreeNode source) {
TreeNode parentNode = source.getParent();
if (parentNode instanceof RepositoryNode) {
addMoreCommits((RepositoryNode)parentNode);
}
}
});
childrenToShown.add(new TextWithLinkNode(moreCommitsLink));
break;
}
childrenToShown.add(commitToNode.fun(commits.get(i)));
}
return childrenToShown;
}
@NotNull
public List<VcsPushOptionsPanel> getAdditionalPanels() {
List<VcsPushOptionsPanel> additionalPanels = new ArrayList<VcsPushOptionsPanel>();
for (final PushSupport support : myPushSupports) {
final VcsPushOptionsPanel panel = support.getVcsPushOptionsPanel();
if (panel != null) {
additionalPanels.add(panel);
myAdditionalValuesMap.put(support, new MyPushOptionValueModel(panel.getValue()));
panel.addValueChangeListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myAdditionalValuesMap.get(support).setCurrentValue(panel.getValue());
}
});
}
}
return additionalPanels;
}
private static class MyRepoModel {
final Repository myRepository;
private PushSupport mySupport;
PushSpec mySpec;
int myNumberOfShownCommits;
List<? extends VcsFullCommitDetails> myLoadedCommits;
boolean myIsSelected;
public MyRepoModel(Repository repository, PushSupport supportForRepo, boolean isSelected, PushSpec spec, int num) {
myRepository = repository;
mySupport = supportForRepo;
myIsSelected = isSelected;
mySpec = spec;
myNumberOfShownCommits = num;
}
public Repository getRepository() {
return myRepository;
}
public PushSupport getSupport() {
return mySupport;
}
public boolean isSelected() {
return myIsSelected;
}
public AbstractVcs<?> getVcs() {
return myRepository.getVcs();
}
public PushSpec getSpec() {
return mySpec;
}
public void setSpec(PushSpec spec) {
mySpec = spec;
}
public void setSelected(boolean isSelected) {
myIsSelected = isSelected;
}
public int getNumberOfShownCommits() {
return myNumberOfShownCommits;
}
public void increaseShownCommits() {
myNumberOfShownCommits *= 2;
}
public List<? extends VcsFullCommitDetails> getLoadedCommits() {
return myLoadedCommits;
}
public void setLoadedCommits(List<? extends VcsFullCommitDetails> loadedCommits) {
myLoadedCommits = loadedCommits;
}
}
private static class MyPushOptionValueModel {
@NotNull private VcsPushOptionValue myCurrentValue;
public MyPushOptionValueModel(@NotNull VcsPushOptionValue currentValue) {
myCurrentValue = currentValue;
}
public void setCurrentValue(@NotNull VcsPushOptionValue currentValue) {
myCurrentValue = currentValue;
}
@NotNull
public VcsPushOptionValue getCurrentValue() {
return myCurrentValue;
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2014 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.dvcs.push;
public interface RepositoryNodeListener {
void onTargetChanged(String newValue);
void onSelectionChanged(boolean isSelected);
}
@@ -0,0 +1,24 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.ui.ColoredTreeCellRenderer;
import org.jetbrains.annotations.NotNull;
interface CustomRenderedTreeNode {
void render(@NotNull ColoredTreeCellRenderer renderer);
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.dvcs.push.VcsPushReferenceStrategy;
import com.intellij.openapi.ui.ComboBox;
import javax.swing.*;
import java.awt.*;
public class DvcsStrategyPanel extends JPanel {
private ComboBox myReferenceStrategyCombobox;
public DvcsStrategyPanel() {
setLayout(new BorderLayout());
myReferenceStrategyCombobox = new ComboBox();
DefaultComboBoxModel comboModel = new DefaultComboBoxModel(VcsPushReferenceStrategy.values());
myReferenceStrategyCombobox.setModel(comboModel);
JPanel bottomPanel = new JPanel(new FlowLayout());
JLabel referenceStrategyLabel = new JLabel("Push Reference Strategy: ");
bottomPanel.add(referenceStrategyLabel, FlowLayout.LEFT);
bottomPanel.add(myReferenceStrategyCombobox);
add(bottomPanel, BorderLayout.WEST);
}
public VcsPushReferenceStrategy getStrategy() {
return (VcsPushReferenceStrategy)myReferenceStrategyCombobox.getSelectedItem();
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.dvcs.push.RepositoryNodeListener;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.annotations.NotNull;
public interface EditableTreeNode extends CustomRenderedTreeNode {
void addRepoNodeListener(@NotNull RepositoryNodeListener listener);
void fireOnChange(@NotNull String value);
void fireOnSelectionChange(boolean isSelected);
void stopLoading();
ProgressIndicator startLoading();
String getValue();
}
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.net.URL;
public class LoadingTreeNode extends DefaultMutableTreeNode implements CustomRenderedTreeNode {
@NotNull protected ImageIcon myLoadingIcon;
private static final String LOADING_ICON = "loadingSimpleIcon.gif";
private static final String LOADING_DARK_ICON = "loadingSimpleDarkIcon.gif";
@NotNull
public ImageIcon getIcon() {
return myLoadingIcon;
}
public LoadingTreeNode() {
super(null, false);
URL loadingIconPath = getClass().getResource(UIUtil.isUnderDarcula() ? LOADING_DARK_ICON : LOADING_ICON);
myLoadingIcon = new ImageIcon(loadingIconPath.getPath());
}
@Override
public void render(@NotNull ColoredTreeCellRenderer renderer) {
renderer.setIcon(myLoadingIcon);
renderer.append("Loading Commits...", new SimpleTextAttributes(SimpleTextAttributes.STYLE_SMALLER, JBColor.GRAY));
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.image.ImageObserver;
class NodeImageObserver implements ImageObserver {
JTree tree;
DefaultTreeModel model;
TreeNode node;
NodeImageObserver(JTree tree, TreeNode node) {
this.tree = tree;
this.model = (DefaultTreeModel)tree.getModel();
this.node = node;
}
public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
if ((flags & (FRAMEBITS | ALLBITS)) != 0) {
TreePath path = new TreePath(model.getPathToRoot(node));
Rectangle rect = tree.getPathBounds(path);
if (rect != null) {
tree.repaint(rect);
}
}
return (flags & (ALLBITS | ABORT)) == 0;
}
}
@@ -0,0 +1,280 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.actionSystem.DataSink;
import com.intellij.openapi.actionSystem.TypeSafeDataProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.ui.*;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.vcs.log.VcsFullCommitDetails;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EventObject;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class PushLog extends JPanel implements TypeSafeDataProvider {
private final ReentrantReadWriteLock TREE_CONSTRUCTION_LOCK = new ReentrantReadWriteLock();
private static final String START_EDITING = "startEditing";
private final ChangesBrowser myChangesBrowser;
private final CheckboxTree myTree;
private final CheckedTreeNode myRootNode;
private final MyTreeCellRenderer myTreeCellRenderer;
public PushLog(Project project, CheckedTreeNode root) {
myRootNode = root;
DefaultTreeModel treeModel = new DefaultTreeModel(myRootNode);
treeModel.nodeStructureChanged(myRootNode);
myTreeCellRenderer = new MyTreeCellRenderer();
myTree = new CheckboxTree(myTreeCellRenderer, myRootNode) {
public boolean isPathEditable(TreePath path) {
return isEditable() && path.getLastPathComponent() instanceof DefaultMutableTreeNode;
}
@Override
protected void onNodeStateChanged(CheckedTreeNode node) {
if (node instanceof EditableTreeNode) {
((EditableTreeNode)node).fireOnSelectionChange(node.isChecked());
}
}
@Override
public String getToolTipText(MouseEvent event) {
final TreePath path = myTree.getPathForLocation(event.getX(), event.getY());
if (path == null) {
return "";
}
Object node = path.getLastPathComponent();
if (node == null || (!(node instanceof DefaultMutableTreeNode))) {
return "";
}
if (node instanceof TooltipNode) {
return ((TooltipNode)node).getTooltip();
}
return "";
}
};
myTree.setEditable(true);
MyTreeCellEditor treeCellEditor = new MyTreeCellEditor(new JBTextField());
myTree.setCellEditor(treeCellEditor);
treeCellEditor.addCellEditorListener(new CellEditorListener() {
@Override
public void editingStopped(ChangeEvent e) {
}
@Override
public void editingCanceled(ChangeEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)myTree.getLastSelectedPathComponent();
if (node != null && node instanceof EditableTreeNode) {
//todo restore from appropriate editor
((EditableTreeNode)node).fireOnChange(((EditableTreeNode)node).getValue());
}
}
});
myTree.setRootVisible(false);
TreeUtil.expandAll(myTree);
final VcsBranchEditorListener linkMouseListener = new VcsBranchEditorListener(myTreeCellRenderer);
linkMouseListener.installOn(myTree);
myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
myTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath[] nodes = myTree.getSelectionPaths();
if (nodes != null) {
ArrayList<Change> changes = new ArrayList<Change>();
for (TreePath node : nodes) {
Object nodeInfo = ((DefaultMutableTreeNode)node.getLastPathComponent()).getUserObject();
if (nodeInfo instanceof VcsFullCommitDetails) {
changes.addAll(((VcsFullCommitDetails)nodeInfo).getChanges());
}
}
myChangesBrowser.getViewer().setEmptyText("No differences");
myChangesBrowser.setChangesToDisplay(CommittedChangesTreeBrowser.zipChanges(changes));
return;
}
setDefaultEmptyText();
myChangesBrowser.setChangesToDisplay(Collections.<Change>emptyList());
}
});
myTree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), START_EDITING);
myTree.setRowHeight(0);
ToolTipManager.sharedInstance().registerComponent(myTree);
myChangesBrowser =
new ChangesBrowser(project, null, Collections.<Change>emptyList(), null, false, true, null, ChangesBrowser.MyUseCase.LOCAL_CHANGES,
null);
myChangesBrowser.getDiffAction().registerCustomShortcutSet(CommonShortcuts.getDiff(), myTree);
setDefaultEmptyText();
Splitter splitter = new Splitter(false, 0.7f);
splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree));
splitter.setSecondComponent(myChangesBrowser);
setLayout(new BorderLayout());
add(splitter);
}
private void setDefaultEmptyText() {
myChangesBrowser.getViewer().setEmptyText("No commits selected");
}
// Make changes available for diff action
@Override
public void calcData(DataKey key, DataSink sink) {
if (VcsDataKeys.CHANGES.equals(key)) {
DefaultMutableTreeNode[] selectedNodes = myTree.getSelectedNodes(DefaultMutableTreeNode.class, null);
if (selectedNodes.length == 0) {
return;
}
Object object = selectedNodes[0].getUserObject();
if (object instanceof VcsFullCommitDetails) {
sink.put(key, ArrayUtil.toObjectArray(((VcsFullCommitDetails)object).getChanges(), Change.class));
}
}
}
@Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
if (e.getKeyCode() == KeyEvent.VK_ENTER && myTree.isEditing()) {
myTree.cancelEditing();
return true;
}
return super.processKeyBinding(ks, e, condition, pressed);
}
public void startLoading(DefaultMutableTreeNode parentNode) {
LoadingTreeNode loading = new LoadingTreeNode();
loading.getIcon().setImageObserver(new NodeImageObserver(myTree, loading));
setChildren(parentNode, Collections.singleton(loading));
}
private class MyTreeCellEditor extends DefaultCellEditor {
public MyTreeCellEditor(JTextField field) {
super(field);
setClickCountToStart(1);
}
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
final Object node = ((DefaultMutableTreeNode)value).getUserObject();
editorComponent =
(JComponent)((RepositoryWithBranchPanel)node).getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, true);
return editorComponent;
}
@Override
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
MouseEvent me = ((MouseEvent)anEvent);
final TreePath path = myTree.getClosestPathForLocation(me.getX(), me.getY());
final int row = myTree.getRowForLocation(me.getX(), me.getY());
myTree.getCellRenderer().getTreeCellRendererComponent(myTree, path.getLastPathComponent(), false, false, true, row, true);
Object tag = me.getClickCount() >= clickCountToStart
? PushLogTreeUtil.getTagAtForRenderer(myTreeCellRenderer, me)
: null;
return tag instanceof EditorTextField;
}
//if keyboard event - then anEvent will be null =( See BasicTreeUi
Object treeNode = myTree.getAnchorSelectionPath().getLastPathComponent();
return treeNode instanceof EditableTreeNode;
}
//Implement the one CellEditor method that AbstractCellEditor doesn't.
public Object getCellEditorValue() {
return ((RepositoryWithBranchPanel)editorComponent).getRemoteTargetName();
}
}
private static class MyTreeCellRenderer extends CheckboxTree.CheckboxTreeCellRenderer {
@Override
public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if (!(value instanceof DefaultMutableTreeNode)) {
return;
}
if (value instanceof RepositoryNode) {
//todo simplify, remove instance of
myCheckbox.setVisible(((RepositoryNode)value).isCheckboxVisible());
}
Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
ColoredTreeCellRenderer renderer = getTextRenderer();
renderer.setBorder(null);
if (value instanceof CustomRenderedTreeNode) {
((CustomRenderedTreeNode)value).render(renderer);
}
else {
renderer.append(userObject == null ? "" : userObject.toString());
}
}
}
public void setChildren(DefaultMutableTreeNode parentNode, @NotNull Collection<? extends DefaultMutableTreeNode> childrenNodes) {
setChildren(parentNode, childrenNodes, true);
}
public void setChildren(DefaultMutableTreeNode parentNode,
@NotNull Collection<? extends DefaultMutableTreeNode> childrenNodes,
boolean shouldExpand) {
try {
TREE_CONSTRUCTION_LOCK.writeLock().lock();
parentNode.removeAllChildren();
for (DefaultMutableTreeNode child : childrenNodes) {
parentNode.add(child);
}
final DefaultTreeModel model = ((DefaultTreeModel)myTree.getModel());
model.nodeStructureChanged(parentNode);
TreePath path = TreeUtil.getPathFromRoot(parentNode);
if (shouldExpand) {
myTree.expandPath(path);
}
else {
myTree.collapsePath(path);
}
}
finally {
TREE_CONSTRUCTION_LOCK.writeLock().unlock();
}
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.ui.CheckboxTree;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseEvent;
public class PushLogTreeUtil {
@Nullable
public static Object getTagAtForRenderer(CheckboxTree.CheckboxTreeCellRenderer renderer, MouseEvent e) {
JTree tree = (JTree)e.getSource();
Object tag = null;
final TreePath path = tree.getPathForLocation(e.getX(), e.getY());
if (path != null) {
final Rectangle rectangle = tree.getPathBounds(path);
assert rectangle != null;
int dx = e.getX() - rectangle.x;
final TreeNode treeNode = (TreeNode)path.getLastPathComponent();
final int row = tree.getRowForLocation(e.getX(), e.getY());
tree.getCellRenderer().getTreeCellRendererComponent(tree, treeNode, false, false, true, row, true);
if (treeNode instanceof RepositoryNode) {
RepositoryNode repositoryNode = (RepositoryNode)treeNode;
int checkBoxWidth = repositoryNode.isCheckboxVisible() ? renderer.getCheckbox().getWidth() : 0;
tag = renderer.getTextRenderer().getFragmentTagAt(dx - checkBoxWidth);
}
else {
tag = renderer.getTextRenderer().getFragmentTagAt(dx);
}
}
return tag;
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.dvcs.push.RepositoryNodeListener;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.List;
public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode {
protected final static String ENTER_REMOTE = "Enter Remote";
private RepositoryWithBranchPanel myRepositoryPanel;
private ProgressIndicator myCurrentIndicator;
private final List<RepositoryNodeListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
public RepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel) {
super(repositoryPanel);
myRepositoryPanel = repositoryPanel;
}
public boolean isCheckboxVisible() {
return true;
}
public void render(@NotNull ColoredTreeCellRenderer renderer) {
String repositoryPath = myRepositoryPanel.getRepositoryName();
renderer.append(repositoryPath, SimpleTextAttributes.GRAY_ATTRIBUTES);
renderer.appendFixedTextFragmentWidth(120);
renderer
.append(myRepositoryPanel.getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
renderer
.append(myRepositoryPanel.getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
EditorTextField textField = myRepositoryPanel.getRemoteTextFiled();
String targetName = myRepositoryPanel.getRemoteTargetName();
if (StringUtil.isEmptyOrSpaces(targetName)) {
renderer.append(ENTER_REMOTE, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES, textField);
}
else {
renderer.append(targetName, SimpleTextAttributes.SYNTHETIC_ATTRIBUTES, textField);
}
Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(textField);
renderer.setBorder(new EmptyBorder(insets));
}
@Override
@NotNull
public String getValue() {
return myRepositoryPanel.getRemoteTargetName();
}
@Override
public void addRepoNodeListener(@NotNull RepositoryNodeListener listener) {
myListeners.add(listener);
}
@Override
public void fireOnChange(@NotNull String newValue) {
for (RepositoryNodeListener listener : myListeners) {
listener.onTargetChanged(newValue);
}
}
@Override
public void fireOnSelectionChange(boolean isSelected) {
for (RepositoryNodeListener listener : myListeners) {
listener.onSelectionChanged(isSelected);
}
}
@Override
public void stopLoading() {
if (myCurrentIndicator != null && myCurrentIndicator.isRunning()) {
myCurrentIndicator.cancel();
}
}
@Override
public ProgressIndicator startLoading() {
return myCurrentIndicator = new EmptyProgressIndicator();
}
}
@@ -0,0 +1,156 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.TextFieldWithAutoCompletion;
import com.intellij.ui.TextFieldWithAutoCompletionListProvider;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.Collection;
public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCellRenderer {
private final JBCheckBox myRepositoryCheckbox;
private final TextFieldWithAutoCompletion myDestBranchTextField;
private final JBLabel myLocalBranch;
private final JLabel myArrowLabel;
private final JLabel myRepositoryLabel;
private final ColoredTreeCellRenderer myTextRenderer;
public RepositoryWithBranchPanel(Project project, @NotNull String repoName,
@NotNull String sourceName, String targetName, @NotNull Collection<String> targetVariants) {
super();
setLayout(new BorderLayout());
myRepositoryCheckbox = new JBCheckBox();
myRepositoryCheckbox.setOpaque(false);
myRepositoryCheckbox.setEnabled(false); //may be return editor depends on focus
myRepositoryLabel = new JLabel(repoName);
myLocalBranch = new JBLabel(sourceName);
myArrowLabel = new JLabel(" -> ");
TextFieldWithAutoCompletionListProvider<String> provider =
new TextFieldWithAutoCompletion.StringsCompletionProvider(targetVariants, null);
myDestBranchTextField = new TextFieldWithAutoCompletion<String>(project, provider, true, targetName) {
@Override
public boolean shouldHaveBorder() {
return false;
}
@Override
protected void updateBorder(@NotNull final EditorEx editor) {
}
};
myDestBranchTextField.setBorder(UIUtil.getTableFocusCellHighlightBorder());//getTextFieldBorder());
myDestBranchTextField.setOneLineMode(true);
myDestBranchTextField.setOpaque(true);
myDestBranchTextField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
myDestBranchTextField.selectAll();
}
});
myTextRenderer = new ColoredTreeCellRenderer() {
public void customizeCellRenderer(@NotNull JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
}
};
myTextRenderer.setOpaque(false);
layoutComponents();
}
private void layoutComponents() {
add(myRepositoryCheckbox, BorderLayout.WEST);
JPanel panel = new NonOpaquePanel(new BorderLayout());
panel.add(myTextRenderer, BorderLayout.WEST);
panel.add(myDestBranchTextField, BorderLayout.CENTER);
add(panel, BorderLayout.CENTER);
}
@NotNull
public String getRepositoryName() {
return myRepositoryLabel.getText();
}
public String getSourceName() {
return myLocalBranch.getText();
}
public String getArrow() {
return myArrowLabel.getText();
}
public TextFieldWithAutoCompletion<String> getRemoteTextFiled() {
return myDestBranchTextField;
}
@NotNull
public String getRemoteTargetName() {
return myDestBranchTextField.getText();
}
@Override
public Component getTreeCellRendererComponent(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
Rectangle bounds = tree.getPathBounds(tree.getPathForRow(row));
invalidate();
if (!(value instanceof SingleRepositoryNode)) {
RepositoryNode node = (RepositoryNode)value;
myRepositoryCheckbox.setSelected(node.isChecked());
myRepositoryCheckbox.setVisible(true);
myTextRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
myTextRenderer.append(getRepositoryName(), SimpleTextAttributes.GRAY_ATTRIBUTES);
myTextRenderer.appendFixedTextFragmentWidth(120);
}
else {
myTextRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
myRepositoryCheckbox.setVisible(false);
}
myTextRenderer.append(getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
myTextRenderer.append(getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
if (bounds != null) {
setPreferredSize(new Dimension(tree.getWidth() - bounds.x, bounds.height));
}
revalidate();
return this;
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.SimpleTextAttributes;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
public class SingleRepositoryNode extends RepositoryNode {
private RepositoryWithBranchPanel myRepositoryPanel;
public SingleRepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel) {
super(repositoryPanel);
myRepositoryPanel = repositoryPanel;
}
public boolean isCheckboxVisible() {
return false;
}
@Override
public void render(@NotNull ColoredTreeCellRenderer renderer) {
renderer
.append(myRepositoryPanel.getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
renderer
.append(myRepositoryPanel.getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
EditorTextField textField = myRepositoryPanel.getRemoteTextFiled();
String targetName = myRepositoryPanel.getRemoteTargetName();
if (StringUtil.isEmptyOrSpaces(targetName)) {
renderer.append(ENTER_REMOTE, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES, textField);
}
else {
renderer.append(targetName, SimpleTextAttributes.SYNTHETIC_ATTRIBUTES, textField);
}
Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(textField);
renderer.setBorder(new EmptyBorder(insets));
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.dvcs.push.VcsLinkedText;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
public class TextWithLinkNode extends DefaultMutableTreeNode implements CustomRenderedTreeNode {
@NotNull protected VcsLinkedText myLinkedText;
public TextWithLinkNode(@NotNull VcsLinkedText linkedText) {
myLinkedText = linkedText;
}
public void fireOnClick(@NotNull TextWithLinkNode relatedNode) {
myLinkedText.fireOnClick(relatedNode);
}
@Override
public void render(@NotNull ColoredTreeCellRenderer renderer) {
renderer.append(myLinkedText.getText(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
String linkedText = myLinkedText.getLinkText();
if (!StringUtil.isEmptyOrSpaces(linkedText)) {
renderer.append(" ");
renderer.append(myLinkedText.getLinkText(), SimpleTextAttributes.SYNTHETIC_ATTRIBUTES, this);
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
public interface TooltipNode {
String getTooltip();
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase;
import com.intellij.ui.CheckboxTree;
import com.intellij.ui.EditorTextField;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.event.MouseEvent;
public class VcsBranchEditorListener extends LinkMouseListenerBase {
private final CheckboxTree.CheckboxTreeCellRenderer myRenderer;
public VcsBranchEditorListener(final CheckboxTree.CheckboxTreeCellRenderer renderer) {
myRenderer = renderer;
}
@Override
public void mouseMoved(MouseEvent e) {
Component component = (Component)e.getSource();
Object tag = getTagAt(e);
if (tag != null && tag instanceof EditorTextField) {
component.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
}
else if (tag != null && tag instanceof TextWithLinkNode) {
component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else {
component.setCursor(Cursor.getDefaultCursor());
}
}
@Nullable
@Override
protected Object getTagAt(@NotNull final MouseEvent e) {
return PushLogTreeUtil.getTagAtForRenderer(myRenderer, e);
}
protected void handleTagClick(@Nullable Object tag, @NotNull MouseEvent event) {
if (tag instanceof TextWithLinkNode) {
TextWithLinkNode textWithLink = (TextWithLinkNode)tag;
textWithLink.fireOnClick(textWithLink);
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.vcs.log.VcsFullCommitDetails;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
public class VcsFullCommitDetailsNode extends DefaultMutableTreeNode implements CustomRenderedTreeNode, TooltipNode {
@NotNull private final Project myProject;
private final VcsFullCommitDetails myCommit;
public VcsFullCommitDetailsNode(@NotNull Project project, VcsFullCommitDetails commit) {
super(commit, false);
myProject = project;
myCommit = commit;
}
@Override
public void render(@NotNull ColoredTreeCellRenderer renderer) {
renderer
.append(myCommit.getSubject(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_SMALLER, renderer.getForeground()));
}
public String getTooltip() {
return DvcsUtil.getShortHash(myCommit.getId().toString()) +
" " +
DvcsUtil.getDateString(myCommit) +
" by " +
myCommit.getAuthor().getName() +
"\n\n" +
IssueLinkHtmlRenderer.formatTextWithLinks(myProject, myCommit.getFullMessage());
}
}
@@ -0,0 +1,128 @@
/*
* Copyright 2000-2014 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.dvcs.push.ui;
import com.intellij.dvcs.push.PushController;
import com.intellij.dvcs.push.VcsPushOptionsPanel;
import com.intellij.dvcs.repo.Repository;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.OptionAction;
import net.miginfocom.swing.MigLayout;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
public class VcsPushDialog extends DialogWrapper {
private final PushLog myListPanel;
private final PushController myController;
private final Action[] myExecutorActions = {new DvcsPushAction("&Force Push", true)};
@NotNull private final JPanel myAdditionalOptionsFromVcsPanel;
private DvcsPushAction myPushAction;
public VcsPushDialog(@NotNull Project project, @NotNull List<? extends Repository> selectedRepositories) {
super(project);
myController = new PushController(project, this, selectedRepositories);
myListPanel = myController.getPushPanelInfo();
myAdditionalOptionsFromVcsPanel = new JPanel(new MigLayout("ins 0 0, flowx"));
init();
setOKButtonText("Push");
setOKButtonMnemonic('P');
setTitle("Push Dialog");
}
@Override
protected JComponent createCenterPanel() {
JComponent rootPanel = new JPanel(new BorderLayout(0, 15));
rootPanel.add(myListPanel, BorderLayout.CENTER);
for (VcsPushOptionsPanel panel : myController.getAdditionalPanels()) {
myAdditionalOptionsFromVcsPanel.add(panel);
}
rootPanel.add(myAdditionalOptionsFromVcsPanel, BorderLayout.SOUTH);
return rootPanel;
}
@Override
protected String getDimensionServiceKey() {
return VcsPushDialog.class.getName();
}
protected void setOKActionEnabled(boolean isEnabled) {
if (myPushAction != null) {
myPushAction.setEnabled(isEnabled);
}
for (Action executorAction : myExecutorActions) {
executorAction.setEnabled(isEnabled);
}
}
@Override
@NotNull
protected Action[] createActions() {
final List<Action> actions = new ArrayList<Action>();
myPushAction = new DvcsPushAction("&Push", false);
myPushAction.putValue(DEFAULT_ACTION, Boolean.TRUE);
actions.add(myPushAction);
myPushAction.setOptions(myExecutorActions);
actions.add(getCancelAction());
actions.add(getHelpAction());
return actions.toArray(new Action[actions.size()]);
}
@NotNull
@Override
protected Action getOKAction() {
return new DvcsPushAction("&Push", false);
}
@Override
protected String getHelpId() {
return "reference.mercurial.push.dialog";
}
private class DvcsPushAction extends AbstractAction implements OptionAction {
private Action[] myOptions = new Action[0];
private final boolean myForce;
private DvcsPushAction(String title, boolean force) {
super(title);
myForce = force;
}
@Override
public void actionPerformed(ActionEvent e) {
myController.push(myForce);
close(OK_EXIT_CODE);
}
@NotNull
@Override
public Action[] getOptions() {
return myOptions;
}
public void setOptions(Action[] actions) {
myOptions = actions;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -15,15 +15,15 @@
*/
package com.intellij.dvcs.test;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.components.BaseComponent;
import org.picocontainer.PicoContainer;
import com.intellij.util.messages.MessageBus;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import org.picocontainer.PicoContainer;
/**
*
@@ -371,6 +371,14 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{
public static Future<?> runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task,
@NotNull final ProgressIndicator progressIndicator,
@Nullable final Runnable continuation) {
return runProcessWithProgressAsynchronously(task, progressIndicator, continuation, ModalityState.NON_MODAL);
}
@NotNull
public static Future<?> runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task,
@NotNull final ProgressIndicator progressIndicator,
@Nullable final Runnable continuation,
@NotNull final ModalityState modalityState) {
if (progressIndicator instanceof Disposable) {
Disposer.register(ApplicationManager.getApplication(), (Disposable)progressIndicator);
}
@@ -397,7 +405,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{
public void run() {
task.onCancel();
}
}, ModalityState.NON_MODAL);
}, modalityState);
}
else {
final Task.NotificationInfo notificationInfo = task.notifyFinished();
@@ -412,7 +420,7 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable{
public void run() {
task.onSuccess();
}
}, ModalityState.NON_MODAL);
}, modalityState);
}
}
};
@@ -101,5 +101,8 @@
<xi:include href="/META-INF/vcs-log-graph.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/dvcs.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:fallback/>
</xi:include>
</idea-plugin>
@@ -10,6 +10,9 @@
<xi:include href="/META-INF/vcs-log-graph.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:fallback/>
</xi:include>
<xi:include href="/META-INF/dvcs.xml" xpointer="xpointer(/idea-plugin/*)">
<xi:fallback/>
</xi:include>
<extensionPoints>
<xi:include href="/META-INF/PlatformExtensionPoints.xml" xpointer="xpointer(/idea-plugin/extensionPoints/*)"/>
+2 -1
View File
@@ -49,7 +49,7 @@
</library>
</orderEntry>
<orderEntry type="library" scope="TEST" name="Groovy" level="project" />
<orderEntry type="module" module-name="dvcs" exported="" />
<orderEntry type="module" module-name="dvcs-impl" exported="" />
<orderEntry type="library" scope="TEST" name="cucumber-jvm" level="project" />
<orderEntry type="library" scope="TEST" name="cucumber-java" level="project" />
<orderEntry type="library" name="JSch" level="project" />
@@ -58,6 +58,7 @@
<orderEntry type="module" module-name="vcs-log-api" />
<orderEntry type="module" module-name="vcs-log-impl" />
<orderEntry type="module" module-name="vcs-log-graph" />
<orderEntry type="module" module-name="dvcs-api" />
</component>
</module>
@@ -18,6 +18,7 @@
<orderEntry type="module" module-name="idea-ui" />
<orderEntry type="module" module-name="openapi" />
<orderEntry type="module" module-name="vcs-impl" />
<orderEntry type="module" module-name="dvcs-api" />
</component>
</module>
@@ -15,6 +15,7 @@
*/
package git4idea;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
@@ -103,7 +104,7 @@ public class GitRevisionNumber implements ShortVcsRevisionNumber {
*/
@NotNull
public String getShortRev() {
return GitUtil.getShortHash(myRevisionHash);
return DvcsUtil.getShortHash(myRevisionHash);
}
/**
@@ -102,7 +102,6 @@ public class GitUtil {
public static final String DOT_GIT = ".git";
private final static Logger LOG = Logger.getInstance(GitUtil.class);
private static final int SHORT_HASH_LENGTH = 8;
public static final Predicate<GitBranchTrackInfo> NOT_NULL_PREDICATE = new Predicate<GitBranchTrackInfo>() {
@Override
@@ -828,17 +827,6 @@ public class GitUtil {
}, "\n");
}
@NotNull
public static String getShortHash(@NotNull String hash) {
if (hash.length() == 0) return "";
if (hash.length() == 40) return hash.substring(0, SHORT_HASH_LENGTH);
if (hash.length() > 40) // revision string encoded with date too
{
return hash.substring(hash.indexOf("[") + 1, SHORT_HASH_LENGTH);
}
return hash;
}
@NotNull
public static String fileOrFolder(@NotNull VirtualFile file) {
if (file.isDirectory()) {
@@ -15,6 +15,7 @@
*/
package git4idea.actions;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
@@ -70,7 +71,7 @@ public class GitCompareWithBranchAction extends DumbAwareAction {
String currentRevision = repository.getCurrentRevision();
LOG.assertTrue(currentRevision != null,
"Current revision is null for " + repository + ". Compare with branch shouldn't be available for fresh repository");
head = GitUtil.getShortHash(currentRevision);
head = DvcsUtil.getShortHash(currentRevision);
}
else {
head = currentBranch.getName();
@@ -15,6 +15,7 @@
*/
package git4idea.history;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.components.ServiceManager;
@@ -116,7 +117,7 @@ public class GitDiffFromHistoryHandler extends BaseDiffFromHistoryHandler<GitFil
@NotNull
@Override
protected String getPresentableName(@NotNull GitFileRevision revision) {
return GitUtil.getShortHash(revision.getHash());
return DvcsUtil.getShortHash(revision.getHash());
}
@NotNull
@@ -301,7 +302,7 @@ public class GitDiffFromHistoryHandler extends BaseDiffFromHistoryHandler<GitFil
@NotNull private final GitFileRevision myParentRevision;
public ShowDiffWithParentAction(@NotNull FilePath filePath, @NotNull GitFileRevision rev, @NotNull GitFileRevision parent) {
super(GitUtil.getShortHash(parent.getHash()));
super(DvcsUtil.getShortHash(parent.getHash()));
myFilePath = filePath;
myRevision = rev;
myParentRevision = parent;
@@ -15,6 +15,7 @@
*/
package git4idea.push;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -219,7 +220,7 @@ public class GitPushDialog extends DialogWrapper {
private static String logMessageForCommits(GitCommitsByRepoAndBranch commitsToPush) {
StringBuilder logMessage = new StringBuilder();
for (GitCommit commit : commitsToPush.getAllCommits()) {
logMessage.append(GitUtil.getShortHash(commit.getId().toString()));
logMessage.append(DvcsUtil.getShortHash(commit.getId().toString()));
}
return logMessage.toString();
}
@@ -29,7 +29,6 @@ import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.ui.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import git4idea.GitBranch;
@@ -336,12 +335,12 @@ class GitPushLog extends JPanel implements TypeSafeDataProvider {
@NotNull
private static String getDateString(@NotNull GitCommit commit) {
return DateFormatUtil.formatPrettyDateTime(commit.getTimestamp()) + " ";
return DvcsUtil.getDateString(commit);
}
@NotNull
private static String getHashString(@NotNull GitCommit commit) {
return GitUtil.getShortHash(commit.getId().toString());
return DvcsUtil.getShortHash(commit.getId().toString());
}
private static class MyTreeCellRenderer extends CheckboxTree.CheckboxTreeCellRenderer {
@@ -19,11 +19,13 @@ import com.intellij.dvcs.repo.RepositoryImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitLocalBranch;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.branch.GitBranchesCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -135,6 +137,12 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
return myInfo.getState();
}
@Nullable
@Override
public AbstractVcs getVcs() {
return GitVcs.getInstance(getProject());
}
/**
* @return local and remote branches in this repository.
*/
@@ -15,6 +15,7 @@
*/
package git4idea.reset;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
@@ -26,7 +27,6 @@ import com.intellij.util.ui.RadioButtonEnumModel;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.xml.util.XmlStringUtil;
import git4idea.GitUtil;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
@@ -127,7 +127,7 @@ public class GitNewResetDialog extends DialogWrapper {
String currentRevision = repository.getCurrentRevision();
assert currentRevision != null;
String text = repository.getCurrentBranch() == null ?
"HEAD (" + GitUtil.getShortHash(currentRevision) + ")" :
"HEAD (" + DvcsUtil.getShortHash(currentRevision) + ")" :
repository.getCurrentBranch().getName();
return "<b>" + text + "</b>";
}
@@ -15,6 +15,7 @@
*/
package git4idea.ui;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.changes.Change;
@@ -28,7 +29,6 @@ import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import com.intellij.util.ui.UIUtil;
import git4idea.GitCommit;
import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -198,7 +198,7 @@ public class GitCommitListPanel extends JPanel implements TypeSafeDataProvider {
}
private static String getHash(GitCommit commit) {
return GitUtil.getShortHash(commit.getId().toString());
return DvcsUtil.getShortHash(commit.getId().toString());
}
private static String getAuthor(GitCommit commit) {
@@ -38,9 +38,9 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
import static com.intellij.dvcs.DvcsUtil.getShortHash;
import static com.intellij.openapi.vcs.Executor.overwrite;
import static com.intellij.openapi.vcs.Executor.touch;
import static git4idea.GitUtil.getShortHash;
import static git4idea.test.GitExecutor.*;
import static git4idea.test.GitTestUtil.USER_EMAIL;
import static git4idea.test.GitTestUtil.USER_NAME;
@@ -3,6 +3,7 @@ package git4idea.log;
import com.intellij.mock.MockVirtualFile;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.util.Function;
@@ -283,6 +284,12 @@ public class GitRefManagerTest extends UsefulTestCase {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public AbstractVcs getVcs() {
return null;
}
@Nullable
@Override
public String getCurrentRevision() {
+2 -1
View File
@@ -18,7 +18,8 @@
<orderEntry type="module" module-name="platform-impl" />
<orderEntry type="module" module-name="util" />
<orderEntry type="library" name="gson" level="project" />
<orderEntry type="module" module-name="dvcs" />
<orderEntry type="module" module-name="dvcs-impl" />
<orderEntry type="module" module-name="dvcs-api" />
<orderEntry type="module" module-name="testFramework" scope="TEST" />
<orderEntry type="module" module-name="xml-openapi" />
<orderEntry type="module" module-name="xml" />
+2 -1
View File
@@ -16,9 +16,10 @@
<orderEntry type="module" module-name="vcs-impl" />
<orderEntry type="module" module-name="platform-impl" />
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="module" module-name="dvcs" />
<orderEntry type="module" module-name="dvcs-impl" />
<orderEntry type="module" module-name="vcs-log-api" />
<orderEntry type="module" module-name="vcs-log-impl" />
<orderEntry type="module" module-name="dvcs-api" />
</component>
</module>
+1
View File
@@ -21,6 +21,7 @@
<vcs name="hg4idea" vcsClass="org.zmlx.hg4idea.HgVcs" displayName="Mercurial" administrativeAreaName=".hg"/>
<checkoutProvider implementation="org.zmlx.hg4idea.provider.HgCheckoutProvider"/>
<vcsRootChecker implementation="org.zmlx.hg4idea.roots.HgRootChecker"/>
<pushSupport implementation="org.zmlx.hg4idea.push.HgPushSupport"/>
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<vcsPopupProvider implementation="org.zmlx.hg4idea.provider.HgQuickListProvider"/>
<logProvider implementation="org.zmlx.hg4idea.log.HgLogProvider"/>
@@ -16,26 +16,18 @@
package org.zmlx.hg4idea;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.action.HgCommandResultNotifier;
import org.zmlx.hg4idea.command.HgPushCommand;
import org.zmlx.hg4idea.execution.HgCommandResult;
import org.zmlx.hg4idea.execution.HgCommandResultHandler;
import org.zmlx.hg4idea.repo.HgRepository;
import org.zmlx.hg4idea.ui.HgPushDialog;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -54,17 +46,17 @@ public class HgPusher {
myProject = project;
}
public void showDialogAndPush (@NotNull Collection<HgRepository> repositories,@Nullable final HgRepository selectedRepo) {
/* public void showDialogAndPush(@NotNull List<? extends Repository> selectedRepositories) {
if (repositories.isEmpty()) {
if (selectedRepositories.isEmpty()) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "No Mercurial repositories in the project", MessageType.ERROR);
return;
}
final AtomicReference<HgPushCommand> pushCommand = new AtomicReference<HgPushCommand>();
final HgPushDialog dialog = new HgPushDialog(myProject, repositories, selectedRepo);
dialog.show();
if (dialog.isOK()) {
pushCommand.set(preparePushCommand(myProject, dialog));
if (d2.isOK()) {
pushCommand.set(preparePushCommand(myProject, dialog));
new Task.Backgroundable(myProject, "Pushing...", false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
@@ -74,9 +66,9 @@ public class HgPusher {
}
}.queue();
}
}
}*/
private static void push(final Project project, HgPushCommand command) {
public static void push(final Project project, HgPushCommand command) {
final VirtualFile repo = command.getRepo();
command.execute(new HgCommandResultHandler() {
@Override
@@ -91,9 +83,11 @@ public class HgPusher {
String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum),
repo.getPresentableName());
VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription);
} else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) {
}
else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) {
VcsNotifier.getInstance(project).notifySuccess("Nothing to push");
} else {
}
else {
new HgCommandResultNotifier(project).notifyError(result, "Push failed",
"Failed to push to [" + repo.getPresentableName() + "]");
}
@@ -29,7 +29,9 @@ import org.zmlx.hg4idea.util.HgUtil;
import javax.swing.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
abstract class HgAbstractGlobalAction extends AnAction {
@@ -48,12 +50,17 @@ abstract class HgAbstractGlobalAction extends AnAction {
if (project == null) {
return;
}
VirtualFile file = event.getData(CommonDataKeys.VIRTUAL_FILE);
HgRepositoryManager repositoryManager = HgUtil.getRepositoryManager(project);
HgRepository repo = file != null ? repositoryManager.getRepositoryForFile(file): HgUtil.getCurrentRepository(project);
VirtualFile[] files = event.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY);
final HgRepositoryManager repositoryManager = HgUtil.getRepositoryManager(project);
List<HgRepository> repositories = repositoryManager.getRepositories();
if (!repositories.isEmpty()) {
execute(project, repositories, repo);
List<HgRepository> selectedRepositories = (files == null || files.length == 0)
?
Collections.singletonList(HgUtil.getCurrentRepository(project))
: HgActionUtil.collectRepositoriesFromFiles(repositoryManager,
Arrays.asList(files));
execute(project, repositories, selectedRepositories);
}
}
@@ -66,7 +73,7 @@ abstract class HgAbstractGlobalAction extends AnAction {
protected abstract void execute(@NotNull Project project,
@NotNull Collection<HgRepository> repositories,
@Nullable HgRepository selectedRepo);
@NotNull List<HgRepository> selectedRepositories);
public static void handleException(@Nullable Project project, @NotNull Exception e) {
handleException(project, "Error", e);
@@ -102,16 +109,4 @@ abstract class HgAbstractGlobalAction extends AnAction {
return true;
}
@Nullable
public static HgRepository getSelectedRepositoryFromEvent(AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) {
return null;
}
VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
HgRepositoryManager repositoryManager = HgUtil.getRepositoryManager(project);
return file != null ? repositoryManager.getRepositoryForFile(file) : HgUtil.getCurrentRepository(project);
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2000-2014 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 org.zmlx.hg4idea.action;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.repo.HgRepository;
import javax.swing.*;
import java.util.Collection;
import java.util.List;
public abstract class HgAbstractGlobalSingleRepoAction extends HgAbstractGlobalAction {
public HgAbstractGlobalSingleRepoAction(Icon icon) {
super(icon);
}
public HgAbstractGlobalSingleRepoAction() {
super();
}
@Override
protected void execute(@NotNull Project project,
@NotNull Collection<HgRepository> repositories,
@NotNull List<HgRepository> selectedRepositories) {
execute(project, repositories, selectedRepositories.isEmpty() ? null : selectedRepositories.get(0));
}
protected abstract void execute(@NotNull Project project,
@NotNull Collection<HgRepository> repositories,
@Nullable HgRepository selectedRepo);
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2014 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 org.zmlx.hg4idea.action;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.sun.istack.internal.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.repo.HgRepository;
import org.zmlx.hg4idea.repo.HgRepositoryManager;
import org.zmlx.hg4idea.util.HgUtil;
import java.util.Collection;
import java.util.List;
public class HgActionUtil {
@NotNull
public static List<HgRepository> collectRepositoriesFromFiles(@NotNull final HgRepositoryManager repositoryManager,
@NotNull Collection<VirtualFile> files) {
return ContainerUtil.mapNotNull(files, new Function<VirtualFile, HgRepository>() {
@Override
public HgRepository fun(VirtualFile file) {
return repositoryManager.getRepositoryForFile(file);
}
});
}
@Nullable
public static HgRepository getSelectedRepositoryFromEvent(AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) {
return null;
}
VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
HgRepositoryManager repositoryManager = HgUtil.getRepositoryManager(project);
return file != null ? repositoryManager.getRepositoryForFile(file) : HgUtil.getCurrentRepository(project);
}
}
@@ -22,7 +22,7 @@ import org.zmlx.hg4idea.repo.HgRepository;
import java.util.Collection;
public class HgBranchesAction extends HgAbstractGlobalAction {
public class HgBranchesAction extends HgAbstractGlobalSingleRepoAction {
@Override
protected void execute(@NotNull Project project, @NotNull Collection<HgRepository> repositories, @Nullable HgRepository selectedRepo) {
@@ -25,7 +25,7 @@ import org.zmlx.hg4idea.util.HgErrorUtil;
import java.util.Collection;
public class HgCreateTagAction extends HgAbstractGlobalAction {
public class HgCreateTagAction extends HgAbstractGlobalSingleRepoAction {
public void execute(@NotNull final Project project,
@NotNull Collection<HgRepository> repositories,
@@ -33,7 +33,7 @@ import org.zmlx.hg4idea.ui.HgMergeDialog;
import java.util.Collection;
public class HgMerge extends HgAbstractGlobalAction {
public class HgMerge extends HgAbstractGlobalSingleRepoAction {
@Override
public void execute(@NotNull final Project project,
@@ -18,10 +18,10 @@ package org.zmlx.hg4idea.action;
import com.intellij.openapi.actionSystem.AnActionEvent;
import org.zmlx.hg4idea.repo.HgRepository;
public abstract class HgProcessRebaseAction extends HgAbstractGlobalAction {
public abstract class HgProcessRebaseAction extends HgAbstractGlobalSingleRepoAction {
protected static boolean isRebasing(AnActionEvent e) {
HgRepository repository = HgAbstractGlobalAction.getSelectedRepositoryFromEvent(e);
HgRepository repository = HgActionUtil.getSelectedRepositoryFromEvent(e);
return repository != null && repository.getState() == HgRepository.State.REBASING;
}
@@ -24,7 +24,7 @@ import org.zmlx.hg4idea.ui.HgPullDialog;
import java.util.Collection;
public class HgPullAction extends HgAbstractGlobalAction {
public class HgPullAction extends HgAbstractGlobalSingleRepoAction {
public HgPullAction() {
super(AllIcons.Actions.CheckOut);
}
@@ -12,14 +12,14 @@
// limitations under the License.
package org.zmlx.hg4idea.action;
import com.intellij.dvcs.push.ui.VcsPushDialog;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.HgPusher;
import org.zmlx.hg4idea.repo.HgRepository;
import java.util.Collection;
import java.util.List;
public class HgPushAction extends HgAbstractGlobalAction {
public HgPushAction() {
@@ -29,7 +29,7 @@ public class HgPushAction extends HgAbstractGlobalAction {
@Override
public void execute(@NotNull final Project project,
@NotNull Collection<HgRepository> repositories,
@Nullable final HgRepository selectedRepo) {
new HgPusher(project).showDialogAndPush(repositories, selectedRepo);
@NotNull final List<HgRepository> selectedRepositories) {
new VcsPushDialog(project, selectedRepositories).show();
}
}
@@ -25,7 +25,7 @@ import org.zmlx.hg4idea.ui.HgRunConflictResolverDialog;
import java.util.Collection;
public class HgRunConflictResolverAction extends HgAbstractGlobalAction {
public class HgRunConflictResolverAction extends HgAbstractGlobalSingleRepoAction {
@Override
public void execute(@NotNull final Project project, @NotNull Collection<HgRepository> repositories, @Nullable HgRepository selectedRepo) {
@@ -29,7 +29,7 @@ import org.zmlx.hg4idea.util.HgErrorUtil;
import java.util.Collection;
public class HgUpdateToAction extends HgAbstractGlobalAction {
public class HgUpdateToAction extends HgAbstractGlobalSingleRepoAction {
@Override
protected void execute(@NotNull Project project, @NotNull Collection<HgRepository> repositories, @Nullable HgRepository selectedRepo) {
@@ -31,7 +31,7 @@ public class HgIdentifyCommand {
public HgCommandResult execute(@NotNull ModalityState state) {
final List<String> arguments = new LinkedList<String>();
arguments.add(source);
final HgRemoteCommandExecutor executor = new HgRemoteCommandExecutor(project, source, state);
final HgRemoteCommandExecutor executor = new HgRemoteCommandExecutor(project, source, state, false);
executor.setSilent(true);
return executor.executeInCurrentThread(null, "identify", arguments);
}
@@ -12,11 +12,19 @@
// limitations under the License.
package org.zmlx.hg4idea.command;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.execution.HgCommandResult;
import org.zmlx.hg4idea.execution.HgRemoteCommandExecutor;
import org.zmlx.hg4idea.log.HgHistoryUtil;
import org.zmlx.hg4idea.util.HgUtil;
import java.util.LinkedList;
import java.util.List;
public class HgOutgoingCommand extends HgRemoteChangesetsCommand {
public HgOutgoingCommand(Project project) {
@@ -27,4 +35,22 @@ public class HgOutgoingCommand extends HgRemoteChangesetsCommand {
protected String getRepositoryUrl(VirtualFile root) {
return HgUtil.getRepositoryDefaultPushPath(project, root);
}
@Nullable
public HgCommandResult execute(@NotNull VirtualFile repo,
@NotNull String template,
@NotNull String source,
@NotNull String destination,
boolean doNotShowAuthorizationRequest) {
List<String> arguments = new LinkedList<String>();
arguments.add("--template");
arguments.add(template);
arguments.add(HgHistoryUtil.prepareParameter("rev", source));
arguments.add(destination);
HgRemoteCommandExecutor commandExecutor =
new HgRemoteCommandExecutor(project, destination, ModalityState.any(), doNotShowAuthorizationRequest);
commandExecutor.setOutputAlwaysSuppressed(true);
return commandExecutor.executeInCurrentThread(repo, "outgoing", arguments);
}
}
@@ -39,10 +39,13 @@ class HgCommandAuthenticator {
private GetPasswordRunnable myGetPassword;
private final Project myProject;
private boolean myForceAuthorization;
//todo replace silent mode and/or force authorization
private boolean mySilentMode;
public HgCommandAuthenticator(Project project, boolean forceAuthorization) {
public HgCommandAuthenticator(Project project, boolean forceAuthorization, boolean silent) {
myProject = project;
myForceAuthorization = forceAuthorization;
mySilentMode = silent;
}
public void saveCredentials() {
@@ -71,7 +74,7 @@ class HgCommandAuthenticator {
}
public boolean promptForAuthentication(Project project, String proposedLogin, String uri, String path, @Nullable ModalityState state) {
GetPasswordRunnable runnable = new GetPasswordRunnable(project, proposedLogin, uri, path, myForceAuthorization);
GetPasswordRunnable runnable = new GetPasswordRunnable(project, proposedLogin, uri, path, myForceAuthorization, mySilentMode);
ApplicationManager.getApplication().invokeAndWait(runnable, state == null ? ModalityState.defaultModalityState() : state);
myGetPassword = runnable;
return runnable.isOk();
@@ -96,19 +99,27 @@ class HgCommandAuthenticator {
@Nullable private String myURL;
private boolean myRememberPassword;
private boolean myForceAuthorization;
private final boolean mySilent;
public GetPasswordRunnable(Project project, String proposedLogin, String uri, String path, boolean forceAuthorization) {
public GetPasswordRunnable(Project project,
String proposedLogin,
String uri,
String path,
boolean forceAuthorization, boolean silent) {
this.myProject = project;
this.myProposedLogin = proposedLogin;
this.myURL = uri + path;
this.myForceAuthorization = forceAuthorization;
mySilent = silent;
}
public void run() {
// find if we've already been here
final HgVcs vcs = HgVcs.getInstance(myProject);
if (vcs == null) { return; }
if (vcs == null) {
return;
}
@NotNull final HgGlobalSettings hgGlobalSettings = vcs.getGlobalSettings();
@Nullable String rememberedLoginsForUrl = null;
@@ -128,11 +139,14 @@ class HgCommandAuthenticator {
final String key = keyForUrlAndLogin(myURL, login);
try {
final PasswordSafeImpl passwordSafe = (PasswordSafeImpl)PasswordSafe.getInstance();
password = passwordSafe.getMemoryProvider().getPassword(myProject, HgCommandAuthenticator.class, key);
if (password == null) {
if (mySilent) {
password = passwordSafe.getMemoryProvider().getPassword(myProject, HgCommandAuthenticator.class, key);
}
else {
password = passwordSafe.getPassword(myProject, HgCommandAuthenticator.class, key);
}
} catch (PasswordSafeException e) {
}
catch (PasswordSafeException e) {
LOG.info("Couldn't get password for key [" + key + "]", e);
}
}
@@ -146,7 +160,13 @@ class HgCommandAuthenticator {
return;
}
final AuthDialog dialog = new AuthDialog(myProject, HgVcsMessages.message("hg4idea.dialog.login.password.required"), HgVcsMessages.message("hg4idea.dialog.login.description", myURL),
if (mySilent) {
ok = false;
return;
}
final AuthDialog dialog = new AuthDialog(myProject, HgVcsMessages.message("hg4idea.dialog.login.password.required"),
HgVcsMessages.message("hg4idea.dialog.login.description", myURL),
login, password, true);
dialog.show();
if (dialog.isOK()) {
@@ -182,5 +202,4 @@ class HgCommandAuthenticator {
private static String keyForUrlAndLogin(String stringUrl, String login) {
return login + ":" + stringUrl;
}
}
@@ -30,16 +30,22 @@ import java.util.List;
public class HgRemoteCommandExecutor extends HgCommandExecutor {
@Nullable private ModalityState myState;
final boolean myIgnoreAuthorizationRequest;
public HgRemoteCommandExecutor(@NotNull Project project, @Nullable String destination) {
this(project, destination, null);
this(project, destination, null, false);
}
public HgRemoteCommandExecutor(@NotNull Project project, @Nullable String destination, boolean ignoreAuthorizationRequest) {
this(project, destination, null, ignoreAuthorizationRequest);
}
public HgRemoteCommandExecutor(@NotNull Project project,
@Nullable String destination,
@Nullable ModalityState state) {
@Nullable ModalityState state, boolean ignoreAuthorizationRequest) {
super(project, destination);
myState = state;
myIgnoreAuthorizationRequest = ignoreAuthorizationRequest;
}
@Nullable
@@ -48,7 +54,7 @@ public class HgRemoteCommandExecutor extends HgCommandExecutor {
HgCommandResult result = executeInCurrentThread(repo, operation, arguments, false);
if (HgErrorUtil.isAuthorizationError(result)) {
if (!myIgnoreAuthorizationRequest && HgErrorUtil.isAuthorizationError(result)) {
if (HgErrorUtil.hasAuthorizationInDestinationPath(myDestination)) {
new HgCommandResultNotifier(myProject)
.notifyError(result, "Authorization failed", "Your hgrc file settings have wrong username or password in [paths].\n" +
@@ -62,11 +68,11 @@ public class HgRemoteCommandExecutor extends HgCommandExecutor {
@Nullable
private HgCommandResult executeInCurrentThread(@Nullable final VirtualFile repo,
@NotNull final String operation,
@Nullable final List<String> arguments,
boolean forceAuthorization) {
@NotNull final String operation,
@Nullable final List<String> arguments,
boolean forceAuthorization) {
PassReceiver passReceiver = new PassReceiver(myProject, forceAuthorization, myState);
PassReceiver passReceiver = new PassReceiver(myProject, forceAuthorization, myIgnoreAuthorizationRequest, myState);
SocketServer passServer = new SocketServer(passReceiver);
try {
int passPort = passServer.start();
@@ -109,11 +115,13 @@ public class HgRemoteCommandExecutor extends HgCommandExecutor {
private final Project myProject;
private HgCommandAuthenticator myAuthenticator;
private boolean myForceAuthorization;
private boolean mySilentMode;
@Nullable private ModalityState myState;
private PassReceiver(Project project, boolean forceAuthorization, @Nullable ModalityState state) {
private PassReceiver(Project project, boolean forceAuthorization, boolean silent, @Nullable ModalityState state) {
myProject = project;
myForceAuthorization = forceAuthorization;
mySilentMode = silent;
myState = state;
}
@@ -129,7 +137,7 @@ public class HgRemoteCommandExecutor extends HgCommandExecutor {
String path = new String(readDataBlock(dataInputStream));
String proposedLogin = new String(readDataBlock(dataInputStream));
HgCommandAuthenticator authenticator = new HgCommandAuthenticator(myProject, myForceAuthorization);
HgCommandAuthenticator authenticator = new HgCommandAuthenticator(myProject, myForceAuthorization, mySilentMode);
boolean ok = authenticator.promptForAuthentication(myProject, proposedLogin, uri, path, myState);
if (ok) {
myAuthenticator = authenticator;
@@ -94,23 +94,33 @@ public class HgHistoryUtil {
}
/**
* <p>Get & parse hg log detailed output with commits, their parents and their changes.</p>
* <p>Get & parse hg log detailed output with commits, their parents and their changes.
* For null destination return log command result</p>
* <p/>
* <p>Warning: this is method is efficient by speed, but don't query too much, because the whole log output is retrieved at once,
* and it can occupy too much memory. The estimate is ~600Kb for 1000 commits.</p>
*/
@NotNull
public static List<? extends VcsFullCommitDetails> history(@NotNull final Project project, @NotNull final VirtualFile root, int limit,
public static List<? extends VcsFullCommitDetails> history(@NotNull final Project project,
@NotNull final VirtualFile root,
int limit,
@NotNull List<String> parameters) throws VcsException {
final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project);
if (factory == null) {
return Collections.emptyList();
}
HgVcs hgvcs = HgVcs.getInstance(project);
assert hgvcs != null;
final HgVersion version = hgvcs.getVersion();
String[] templates = HgBaseLogParser.constructFullTemplateArgument(true, version);
HgCommandResult result = getLogResult(project, root, version, limit, parameters, HgChangesetUtil.makeTemplate(templates));
return createFullCommitsFromResult(project, root, result, version);
}
public static List<? extends VcsFullCommitDetails> createFullCommitsFromResult(@NotNull Project project,
@NotNull VirtualFile root,
@Nullable HgCommandResult result,
@NotNull HgVersion version) {
final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project);
if (factory == null) {
return Collections.emptyList();
}
List<HgFileRevision> hgRevisions =
getCommitRecords(project, result, new HgFileRevisionLogParser(project, getOriginalHgFile(project, root), version));
List<VcsFullCommitDetails> vcsFullCommitDetailsList = new ArrayList<VcsFullCommitDetails>();
@@ -331,4 +341,8 @@ public class HgHistoryUtil {
}
return branchHeads;
}
public static String prepareParameter(String paramName, String value) {
return "--" + paramName + "=" + value; // no value escaping needed, because the parameter itself will be quoted by GeneralCommandLine
}
}
@@ -180,11 +180,11 @@ public class HgLogProvider implements VcsLogProvider {
boolean atLeastOneBranchExists = false;
for (String branchName : filterCollection.getBranchFilter().getBranchNames()) {
if (branchName.equals(TIP_REFERENCE) || branchExists(repository, branchName)) {
filterParameters.add(prepareParameter("branch", branchName));
filterParameters.add(HgHistoryUtil.prepareParameter("branch", branchName));
atLeastOneBranchExists = true;
}
else if (branchName.equals(HEAD_REFERENCE)) {
filterParameters.add(prepareParameter("branch", "."));
filterParameters.add(HgHistoryUtil.prepareParameter("branch", "."));
filterParameters.add("-r");
filterParameters.add("::."); //all ancestors for current revision;
atLeastOneBranchExists = true;
@@ -197,7 +197,7 @@ public class HgLogProvider implements VcsLogProvider {
if (filterCollection.getUserFilter() != null) {
for (String authorName : filterCollection.getUserFilter().getUserNames(root)) {
filterParameters.add(prepareParameter("user", authorName));
filterParameters.add(HgHistoryUtil.prepareParameter("user", authorName));
}
}
@@ -223,7 +223,7 @@ public class HgLogProvider implements VcsLogProvider {
if (filterCollection.getTextFilter() != null) {
String textFilter = filterCollection.getTextFilter().getText();
filterParameters.add(prepareParameter("keyword", textFilter));
filterParameters.add(HgHistoryUtil.prepareParameter("keyword", textFilter));
}
if (filterCollection.getStructureFilter() != null) {
@@ -262,10 +262,6 @@ public class HgLogProvider implements VcsLogProvider {
return HgHistoryUtil.getDescendingHeadsOfBranches(myProject, root, commitHash);
}
private static String prepareParameter(String paramName, String value) {
return "--" + paramName + "=" + value; // no value escaping needed, because the parameter itself will be quoted by GeneralCommandLine
}
private static boolean branchExists(@NotNull HgRepository repository, @NotNull String branchName) {
return repository.getBranches().keySet().contains(branchName) ||
HgUtil.getNamesWithoutHashes(repository.getBookmarks()).contains(branchName);
@@ -13,6 +13,7 @@
package org.zmlx.hg4idea.provider.commit;
import com.intellij.dvcs.DvcsCommitAdditionalComponent;
import com.intellij.dvcs.push.ui.VcsPushDialog;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -37,6 +38,7 @@ import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.*;
import org.zmlx.hg4idea.action.HgActionUtil;
import org.zmlx.hg4idea.command.*;
import org.zmlx.hg4idea.execution.HgCommandException;
import org.zmlx.hg4idea.execution.HgCommandExecutor;
@@ -132,13 +134,12 @@ public class HgCheckinEnvironment implements CheckinEnvironment {
// push if needed
if (myNextCommitIsPushed && exceptions.isEmpty()) {
final VirtualFile preselectedRepo = repositoriesMap.size() == 1 ? repositoriesMap.keySet().iterator().next() : null;
final Set<VirtualFile> preselectedFiles = repositoriesMap.keySet();
HgRepositoryManager repositoryManager = HgUtil.getRepositoryManager(myProject);
final HgRepository repo = preselectedRepo != null ? repositoryManager.getRepositoryForFile(preselectedRepo) : null;
final Collection<HgRepository> repositories = repositoryManager.getRepositories();
final List<HgRepository> preselectedRepositories = HgActionUtil.collectRepositoriesFromFiles(repositoryManager, preselectedFiles);
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
new HgPusher(myProject).showDialogAndPush(repositories, repo);
new VcsPushDialog(myProject, preselectedRepositories).show();
}
});
}

Some files were not shown because too many files have changed in this diff Show More