diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index 76da573725a8..e1fdc3840f2a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -1173,7 +1173,7 @@ public class GenericsHighlightUtil { public static HighlightInfo checkEnumMustNotBeLocal(final PsiClass aClass) { if (!aClass.isEnum()) return null; PsiElement parent = aClass.getParent(); - if (!(parent instanceof PsiClass || parent instanceof PsiFile)) { + if (!(parent instanceof PsiClass || parent instanceof PsiFile || parent instanceof PsiClassLevelDeclarationStatement)) { String description = JavaErrorMessages.message("local.enum"); TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index 747cea82df93..f30da0b39b4b 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -450,6 +450,9 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data else { nothingToSearchFor(); } + if (mySearchField instanceof JTextArea) { + UIUtil.adjustRows((JTextArea)mySearchField, 2, 6); + } } public boolean isRegexp() { @@ -637,6 +640,9 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data private void replaceFieldDocumentChanged() { setMatchesLimit(LivePreviewController.MATCHES_LIMIT); myFindModel.setStringToReplace(myReplaceField.getText()); + if (myReplaceField instanceof JTextArea) { + UIUtil.adjustRows((JTextArea)myReplaceField, 2, 6); + } } private boolean canReplaceCurrent() { @@ -697,12 +703,17 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data super.paintBorder(g); paintBorderOfTextField(g); } + + @Override + public Dimension getPreferredSize() { + return super.getPreferredSize(); + } }; ((JTextArea)editorTextField).setColumns(25); - ((JTextArea)editorTextField).setRows(3); + ((JTextArea)editorTextField).setRows(2); final JScrollPane scrollPane = new JBScrollPane(editorTextField, - ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); myLeftComponent.add(scrollPane, constraint); componentRef.set(scrollPane); } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index f81859417580..22f5eddf54c8 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -40,6 +40,8 @@ public interface IdeActions { @NonNls String ACTION_EDITOR_BACKSPACE = "EditorBackSpace"; @NonNls String ACTION_EDITOR_MOVE_CARET_LEFT_WITH_SELECTION = "EditorLeftWithSelection"; @NonNls String ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION = "EditorRightWithSelection"; + @NonNls String ACTION_EDITOR_MOVE_CARET_UP_WITH_SELECTION = "EditorUpWithSelection"; + @NonNls String ACTION_EDITOR_MOVE_CARET_DOWN_WITH_SELECTION = "EditorDownWithSelection"; @NonNls String ACTION_EDITOR_MOVE_CARET_UP = "EditorUp"; @NonNls String ACTION_EDITOR_MOVE_CARET_LEFT = "EditorLeft"; @NonNls String ACTION_EDITOR_MOVE_CARET_DOWN = "EditorDown"; diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java index 4b1d9dfafaf9..e3fe9f582393 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java @@ -117,7 +117,7 @@ public class DarculaRadioButtonUI extends MetalRadioButtonUI { if (b.isSelected()) { final boolean enabled = b.isEnabled(); g.setColor(UIManager.getColor(enabled ? "RadioButton.darcula.selectionEnabledShadowColor" : "RadioButton.darcula.selectionDisabledShadowColor"));// ? Gray._30 : Gray._60); - final int yOff = UIUtil.isUnderDarcula() ? 2 : JBUI.scale(1); + final int yOff = 2; g.fillOval(w/2 - rad/2, h/2 - rad/2 + yOff , rad, rad); g.setColor(UIManager.getColor(enabled ? "RadioButton.darcula.selectionEnabledColor" : "RadioButton.darcula.selectionDisabledColor")); //Gray._170 : Gray._120); g.fillOval(w/2 - rad/2, h/2 - rad/2 -1 + yOff, rad, rad); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index fd49b3b5574d..233f0cc45621 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -248,6 +248,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { myEditor.getCaretModel().doWithCaretMerging(new Runnable() { @Override public void run() { + int oldOffset = myOffset; final int leadSelectionOffset = getLeadSelectionOffset(); final VisualPosition leadSelectionPosition = getLeadSelectionPosition(); EditorSettings editorSettings = myEditor.getSettings(); @@ -370,7 +371,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { else { int selectionStartToUse = leadSelectionOffset; VisualPosition selectionStartPositionToUse = leadSelectionPosition; - if (isUnknownDirection()) { + if (isUnknownDirection() || oldOffset > getSelectionStart() && oldOffset < getSelectionEnd()) { if (getOffset() > leadSelectionOffset ^ getSelectionStart() < getSelectionEnd()) { selectionStartToUse = getSelectionEnd(); selectionStartPositionToUse = getSelectionEndPosition(); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java index a653da599d80..2545df56d20c 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java @@ -167,4 +167,24 @@ public class EditorActionTest extends AbstractEditorTest { executeAction(IdeActions.ACTION_EDITOR_DELETE_TO_WORD_END); checkResultByText("class Foo { String s = \"a\\b\"; }"); } + + public void testUpWithSelectionOnCaretInsideSelection() throws Exception { + initText("blah blah\n" + + "blah blah\n" + + "blah blah"); + executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_UP_WITH_SELECTION); + checkResultByText("blah blah\n" + + "blah blah\n" + + "blah blah"); + } + + public void testDownWithSelectionOnCaretInsideSelection() throws Exception { + initText("blah blah\n" + + "blah blah\n" + + "blah blah"); + executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN_WITH_SELECTION); + checkResultByText("blah blah\n" + + "blah blah\n" + + "blah blah"); + } } \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 02bed0eedf02..6af544af7755 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3215,6 +3215,10 @@ public class UIUtil { textComponent.getActionMap().put("redoKeystroke", REDO_ACTION); } + public static void adjustRows(JTextArea area, int minRows, int maxRows) { + area.setRows(Math.max(minRows, Math.min(maxRows, area.getText().split("\n").length))); + } + public static void playSoundFromResource(final String resourceName) { final Class callerClass = ReflectionUtil.getGrandCallerClass(); if (callerClass == null) return; diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java index 29f63e6cdeb3..73b0c3bfdb54 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Collection; import java.util.List; +import java.util.concurrent.Future; /** * Use this interface to access information available in the VCS Log. @@ -55,7 +56,8 @@ public interface VcsLog { /** * Selects the commit node defined by the given reference (commit hash, branch or tag). */ - void jumpToReference(String reference); + @NotNull + Future jumpToReference(String reference); /** * Returns the VCS log toolbar component. @@ -68,5 +70,4 @@ public interface VcsLog { */ @NotNull Collection getLogProviders(); - } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java index e80032bbd9ba..0b685dffc3f1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Collection; import java.util.List; +import java.util.concurrent.Future; /** * @@ -84,8 +85,9 @@ public class VcsLogImpl implements VcsLog { return myUi.getDataPack().getRefsModel().getAllRefs(); } + @NotNull @Override - public void jumpToReference(final String reference) { + public Future jumpToReference(final String reference) { Collection references = getAllReferences(); VcsRef ref = ContainerUtil.find(references, new Condition() { @Override @@ -94,10 +96,10 @@ public class VcsLogImpl implements VcsLog { } }); if (ref != null) { - myUi.jumpToCommit(ref.getCommitHash()); + return myUi.jumpToCommit(ref.getCommitHash()); } else { - myUi.jumpToCommitByPartOfHash(reference); + return myUi.jumpToCommitByPartOfHash(reference); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/FindPopupWithProgress.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/FindPopupWithProgress.java new file mode 100644 index 000000000000..0b46160165ad --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/FindPopupWithProgress.java @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.JBPopupListener; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.Function; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.Collection; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class FindPopupWithProgress { + private static final Logger LOG = Logger.getInstance(FindPopupWithProgress.class); + + @NotNull private final TextFieldWithProgress myTextField; + @NotNull private final Function myFunction; + @NotNull private final JBPopup myPopup; + @Nullable private Future myFuture; + + public FindPopupWithProgress(@NotNull final Project project, + @NotNull Collection variants, + @NotNull Function function) { + myFunction = function; + myTextField = new TextFieldWithProgress(project, variants) { + @Override + public void onOk() { + if (myFuture == null) { + final Future future = myFunction.fun(getText().trim()); + myFuture = future; + showProgress(); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + future.get(); + ok(); + } + catch (CancellationException ex) { + cancel(); + } + catch (InterruptedException ex) { + cancel(); + } + catch (ExecutionException ex) { + LOG.error(ex); + cancel(); + } + } + }); + } + } + }; + + myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(myTextField, myTextField.getPreferableFocusComponent()) + .setCancelOnClickOutside(true).setCancelOnWindowDeactivation(true).setCancelKeyEnabled(true).setRequestFocus(true).createPopup(); + myPopup.addListener(new JBPopupListener.Adapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + if (!event.isOk()) { + if (myFuture != null) { + myFuture.cancel(false); + myFuture = null; + } + } + } + }); + + final JBTextField field = new JBTextField(20); + final Dimension size = field.getPreferredSize(); + final Insets insets = myTextField.getBorder().getBorderInsets(myTextField); + size.height += 6 + insets.top + insets.bottom; + size.width += 4 + insets.left + insets.right; + myPopup.setSize(size); + } + + private void cancel() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (myFuture != null) myFuture = null; + myTextField.hideProgress(); + myPopup.cancel(); + } + }); + } + + private void ok() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (myFuture != null) myFuture = null; + myTextField.hideProgress(); + myPopup.closeOk(null); + } + }); + } + + public void showUnderneathOf(@NotNull Component anchor) { + myPopup.showUnderneathOf(anchor); + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java index 8b117c72da26..12f6b65a0551 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java @@ -18,9 +18,6 @@ package com.intellij.vcs.log.ui; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupListener; -import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsLog; @@ -28,6 +25,7 @@ import com.intellij.vcs.log.VcsLogDataKeys; import com.intellij.vcs.log.VcsRef; import java.util.Collection; +import java.util.concurrent.Future; public class GoToRefAction extends DumbAwareAction { @@ -45,16 +43,12 @@ public class GoToRefAction extends DumbAwareAction { return ref.getName(); } }); - final PopupWithTextFieldWithAutoCompletion textField = new PopupWithTextFieldWithAutoCompletion(project, refs); - JBPopup popup = textField.createPopup(); - popup.addListener(new JBPopupListener.Adapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - if (event.isOk()) { - log.jumpToReference(textField.getText().trim()); - } - } - }); + FindPopupWithProgress popup = new FindPopupWithProgress(project, refs, new Function() { + @Override + public Future fun(String text) { + return log.jumpToReference(text); + } + }); popup.showUnderneathOf(log.getToolbar()); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java deleted file mode 100644 index fbfb71de3c04..000000000000 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2000-2013 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.vcs.log.ui; - -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; -import com.intellij.ui.TextFieldWithAutoCompletion; -import com.intellij.ui.components.JBTextField; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.util.Collection; - -public class PopupWithTextFieldWithAutoCompletion extends TextFieldWithAutoCompletion { - - @Nullable private JBPopup myPopup; - - public PopupWithTextFieldWithAutoCompletion(@NotNull Project project, @NotNull Collection variants) { - super(project, new StringsCompletionProvider(variants, null), false, null); - setBorder(new EmptyBorder(3, 3, 3, 3)); - } - - public JBPopup createPopup() { - myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(this, this) - .setCancelOnClickOutside(true) - .setCancelOnWindowDeactivation(true) - .setCancelKeyEnabled(true) - .setRequestFocus(true) - .createPopup(); - - final JBTextField field = new JBTextField(20); - final Dimension size = field.getPreferredSize(); - final Insets insets = getBorder().getBorderInsets(this); - size.height+=6 + insets.top + insets.bottom; - size.width +=4 + insets.left + insets.right; - myPopup.setSize(size); - - return myPopup; - } - - @Override - protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - if (myPopup != null) { - myPopup.closeOk(e); - } - return true; - } - else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - if (myPopup != null) { - myPopup.cancel(e); - } - return true; - } - return false; - } - - @Override - protected EditorEx createEditor() { - // spell check is not needed - EditorEx editor = super.createEditor(); - SpellCheckingEditorCustomization.getInstance(false).customize(editor); - return editor; - } -} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/TextFieldWithProgress.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/TextFieldWithProgress.java new file mode 100644 index 000000000000..70901fe4caaa --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/TextFieldWithProgress.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui; + +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.progress.PerformInBackgroundOption; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.TextFieldWithAutoCompletion; +import com.intellij.util.ui.AsyncProcessIcon; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.util.Collection; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public abstract class TextFieldWithProgress extends JPanel { + @NotNull private final TextFieldWithAutoCompletion myTextField; + @NotNull private final AsyncProcessIcon myProgressIcon; + + public TextFieldWithProgress(@NotNull Project project, @NotNull Collection variants) { + super(new BorderLayout()); + setBorder(IdeBorderFactory.createEmptyBorder(3)); + + myProgressIcon = new AsyncProcessIcon("Loading commits"); + myTextField = + new TextFieldWithAutoCompletion(project, new TextFieldWithAutoCompletion.StringsCompletionProvider(variants, null), false, + null) { + @Override + public void setBackground(Color bg) { + super.setBackground(bg); + myProgressIcon.setBackground(bg); + } + + @Override + protected EditorEx createEditor() { + // spell check is not needed + EditorEx editor = super.createEditor(); + SpellCheckingEditorCustomization.getInstance(false).customize(editor); + return editor; + } + + @Override + protected boolean processKeyBinding(KeyStroke ks, final KeyEvent e, int condition, boolean pressed) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + onOk(); + return true; + } + return false; + } + }; + myTextField.setBorder(IdeBorderFactory.createEmptyBorder()); + + myProgressIcon.setOpaque(true); + myProgressIcon.setBackground(myTextField.getBackground()); + + add(myTextField, BorderLayout.CENTER); + add(myProgressIcon, BorderLayout.EAST); + + hideProgress(); + } + + public JComponent getPreferableFocusComponent() { + return myTextField; + } + + public void showProgress() { + myTextField.setEnabled(false); + myProgressIcon.setVisible(true); + } + + public void hideProgress() { + myTextField.setEnabled(true); + myProgressIcon.setVisible(false); + } + + public String getText() { + return myTextField.getText(); + } + + public abstract void onOk(); +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java index 78ce9caeb2a1..440b3efd1a9a 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java @@ -1,5 +1,6 @@ package com.intellij.vcs.log.ui; +import com.google.common.util.concurrent.SettableFuture; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -32,6 +33,7 @@ import javax.swing.table.TableModel; import java.awt.*; import java.util.ArrayList; import java.util.Collection; +import java.util.concurrent.Future; public class VcsLogUiImpl implements VcsLogUi, Disposable { @@ -192,22 +194,28 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { return myUiProperties.isShowRootNames(); } - public void jumpToCommit(@NotNull Hash commitHash) { + @NotNull + public Future jumpToCommit(@NotNull Hash commitHash) { + SettableFuture future = SettableFuture.create(); jumpTo(commitHash, new PairFunction() { @Override public Integer fun(GraphTableModel model, Hash hash) { return model.getRowOfCommit(hash); } - }); + }, future); + return future; } - public void jumpToCommitByPartOfHash(@NotNull String commitHash) { + @NotNull + public Future jumpToCommitByPartOfHash(@NotNull String commitHash) { + SettableFuture future = SettableFuture.create(); jumpTo(commitHash, new PairFunction() { @Override public Integer fun(GraphTableModel model, String hash) { return model.getRowOfCommitByPartOfHash(hash); } - }); + }, future); + return future; } public void handleAnswer(@Nullable GraphAnswer answer, boolean dataCouldChange) { @@ -235,13 +243,15 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { } } - private void jumpTo(@NotNull final T commitId, @NotNull final PairFunction rowGetter) { + private void jumpTo(@NotNull final T commitId, @NotNull final PairFunction rowGetter, @NotNull final SettableFuture future) { + if (future.isCancelled()) return; + GraphTableModel model = getModel(); if (model == null) { invokeOnChange(new Runnable() { @Override public void run() { - jumpTo(commitId, rowGetter); + jumpTo(commitId, rowGetter, future); } }); return; @@ -250,12 +260,13 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { int row = rowGetter.fun(model, commitId); if (row >= 0) { myMainFrame.getGraphTable().jumpToRow(row); + future.set(true); } else if (model.canRequestMore()) { model.requestToLoadMore(new Runnable() { @Override public void run() { - jumpTo(commitId, rowGetter); + jumpTo(commitId, rowGetter, future); } }); } @@ -263,12 +274,13 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { invokeOnChange(new Runnable() { @Override public void run() { - jumpTo(commitId, rowGetter); + jumpTo(commitId, rowGetter, future); } }); } else { commitNotFound(commitId.toString()); + future.set(false); } } diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java b/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java index 1d7db804019b..0d4c66a44f8e 100644 --- a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java +++ b/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java @@ -2,6 +2,11 @@ package git4idea.history.wholeTree; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.PerformInBackgroundOption; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -22,7 +27,12 @@ import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + public class SelectRevisionInGitLogAction extends DumbAwareAction { + private static final Logger LOG = Logger.getInstance(SelectRevisionInGitLogAction.class); public SelectRevisionInGitLogAction() { super(GitBundle.getString("vcs.history.action.gitlog"), GitBundle.getString("vcs.history.action.gitlog"), null); @@ -30,7 +40,7 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { @Override public void actionPerformed(@NotNull AnActionEvent event) { - Project project = event.getRequiredData(CommonDataKeys.PROJECT); + final Project project = event.getRequiredData(CommonDataKeys.PROJECT); final VcsRevisionNumber revision = getRevisionNumber(event); if (revision == null) { return; @@ -60,7 +70,7 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { Runnable selectCommit = new Runnable() { @Override public void run() { - log.jumpToReference(revision.asString()); + jumpToRevisionUnderProgress(project, log, revision); } }; @@ -128,5 +138,25 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { return null; } - + private static void jumpToRevisionUnderProgress(@NotNull Project project, @NotNull VcsLog log, @NotNull VcsRevisionNumber revision) { + final Future future = log.jumpToReference(revision.asString()); + if (!future.isDone()) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, "Searching for revision " + revision.asString(), false/*can not cancel*/, + PerformInBackgroundOption.ALWAYS_BACKGROUND) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + future.get(); + } + catch (CancellationException ignored) { + } + catch (InterruptedException ignored) { + } + catch (ExecutionException e) { + LOG.error(e); + } + } + }); + } + } }