diff --git a/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java b/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java index 188c0ef7e06a..babae2d55058 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java @@ -306,7 +306,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { final PsiCodeBlock body = method.getBody(); if (body == null) return; - final Collection exceptionTypes = ExceptionUtil.collectUnhandledExceptions(method, body); + final Collection exceptionTypes = ExceptionUtil.collectUnhandledExceptions(body, method); for (final PsiClassType exceptionType : exceptionTypes) { updateThrowsList(exceptionType); } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index 713bde2f8a4f..63471b258286 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -290,7 +290,7 @@ public class ExpressionParser { @Nullable private static PsiBuilder.Marker parsePostfix(final PsiBuilder builder) { - PsiBuilder.Marker operand = parsePrimary(builder, null); + PsiBuilder.Marker operand = parsePrimary(builder, null, -1); if (operand == null) return null; while (POSTFIX_OPS.contains(builder.getTokenType())) { @@ -306,7 +306,7 @@ public class ExpressionParser { private enum BreakPoint {P1, P2, P3, P4} @Nullable - private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final BreakPoint breakPoint) { + private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final BreakPoint breakPoint, final int breakOffset) { PsiBuilder.Marker startMarker = builder.mark(); PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder); @@ -323,19 +323,20 @@ public class ExpressionParser { final IElementType dotTokenType = builder.getTokenType(); if (dotTokenType == JavaTokenType.CLASS_KEYWORD && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { - if (breakPoint == BreakPoint.P1) { + if (breakPoint == BreakPoint.P1 && builder.getCurrentOffset() == breakOffset) { error(builder, JavaErrorMessages.message("expected.identifier")); PsiBuilderUtil.drop(startMarker, dotPos); return expr; } final PsiBuilder.Marker copy = startMarker.precede(); + final int offset = builder.getCurrentOffset(); startMarker.rollbackTo(); final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); - if (classObjAccess == null) { + if (classObjAccess == null || builder.getCurrentOffset() < offset) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P1); + return parsePrimary(builder, BreakPoint.P1, offset); } startMarker = copy; @@ -359,13 +360,13 @@ public class ExpressionParser { final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, false, true, false, false); if (ref == null || builder.getTokenType() != JavaTokenType.DOT) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P2); + return parsePrimary(builder, BreakPoint.P2, -1); } builder.advanceLexer(); if (builder.getTokenType() != dotTokenType) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P2); + return parsePrimary(builder, BreakPoint.P2, -1); } builder.advanceLexer(); @@ -422,7 +423,7 @@ public class ExpressionParser { } copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P3); + return parsePrimary(builder, BreakPoint.P3, -1); } else { startMarker.drop(); @@ -451,7 +452,7 @@ public class ExpressionParser { final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); if (classObjAccess == null || builder.getCurrentOffset() <= pos) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P4); + return parsePrimary(builder, BreakPoint.P4, -1); } startMarker = copy; @@ -828,16 +829,10 @@ public class ExpressionParser { if (type == JavaTokenType.GTGTGTEQ) { PsiBuilderUtil.advance(builder, 4); } - else if (type == JavaTokenType.GTGTGT) { + else if (type == JavaTokenType.GTGTGT || type == JavaTokenType.GTGTEQ) { PsiBuilderUtil.advance(builder, 3); } - else if (type == JavaTokenType.GTGTEQ) { - PsiBuilderUtil.advance(builder, 3); - } - else if (type == JavaTokenType.GTGT) { - PsiBuilderUtil.advance(builder, 2); - } - else if (type == JavaTokenType.GE) { + else if (type == JavaTokenType.GTGT || type == JavaTokenType.GE) { PsiBuilderUtil.advance(builder, 2); } else { diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java index 6b5d60f42471..cc478cc9dc29 100644 --- a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java @@ -235,6 +235,9 @@ public class JavaSdkImpl extends JavaSdk { VirtualFile commonDocs = findDocs(jdkHome, "docs"); if (commonDocs == null) { commonDocs = findInJar(new File(jdkHome, "docs.jar"), "doc/api"); + if (commonDocs == null) { + commonDocs = findInJar(new File(jdkHome, "docs.jar"), "docs/api"); + } } if (commonDocs != null) { sdkModificator.addRoot(commonDocs, JavadocOrderRootType.getInstance()); diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 33e563fc79cb..b6e09cc8b234 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -565,11 +565,15 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } else if (myRole1 == ChildRole.LOOP_BODY || myChild2.getElementType() == JavaElementType.CODE_BLOCK) { - processOnNewLineCondition(mySettings.WHILE_ON_NEW_LINE); + processOnNewLineCondition(mySettings.WHILE_ON_NEW_LINE, mySettings.SPACE_BEFORE_WHILE_KEYWORD); } } private void processOnNewLineCondition(final boolean onNewLine) { + processOnNewLineCondition(onNewLine, true); + } + + private void processOnNewLineCondition(final boolean onNewLine, final boolean createSpaceInline) { if (onNewLine) { if (!mySettings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE) { myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); @@ -580,7 +584,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } else { - createSpaceProperty(true, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); + createSpaceProperty(createSpaceInline, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } } @@ -597,7 +601,9 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { if (putRightChildOnNewLine) { processOnNewLineCondition(true); } else { - createSpaceProperty(true, false, 0); + boolean useSpace = (myRole2 == ChildRole.CATCH_SECTION && mySettings.SPACE_BEFORE_CATCH_KEYWORD) + || (myRole2 == ChildRole.FINALLY_KEYWORD && mySettings.SPACE_BEFORE_FINALLY_KEYWORD); + createSpaceProperty(useSpace, false, 0); } return; } @@ -747,7 +753,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } else { - createSpaceProperty(true, false, 0); + createSpaceProperty(mySettings.SPACE_BEFORE_ELSE_KEYWORD, false, 0); } } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 8f1dd139c86d..4ce85e1d9090 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -41,6 +41,8 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.CharTable; import com.intellij.util.diff.FlyweightCapableTreeStructure; +import static com.intellij.lang.PsiBuilderUtil.expect; + public interface JavaElementType { IElementType CLASS = JavaStubElementTypes.CLASS; IElementType ANONYMOUS_CLASS = JavaStubElementTypes.ANONYMOUS_CLASS; @@ -252,6 +254,7 @@ public interface JavaElementType { new JavaParserUtil.ParserWrapper() { public void parse(final PsiBuilder builder) { ReferenceParser.parseType(builder); + expect(builder, JavaTokenType.ELLIPSIS); } }); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 18202cd2b853..1440f58bcd64 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -64,6 +64,8 @@ import com.intellij.refactoring.util.RefactoringUIUtil; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.occurences.ExpressionOccurenceManager; import com.intellij.refactoring.util.occurences.NotInSuperCallOccurenceFilter; +import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.*; import org.jetbrains.annotations.NonNls; @@ -477,7 +479,22 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme if (elementToRename != null) { editor.getCaretModel().moveToOffset(elementToRename.getTextOffset()); new VariableInplaceRenamer(elementToRename, editor) - .performInplaceRename(false, new LinkedHashSet(Arrays.asList(suggestedName.names)), exprMarker); + .performInplaceRename(false, new LinkedHashSet(Arrays.asList(suggestedName.names)), new Consumer(){ + @Override + public void consume(Boolean apply) { + if (apply) { + int startOffset = exprMarker.getStartOffset(); + final PsiReference referenceAt = file.findReferenceAt(startOffset); + if (referenceAt != null && referenceAt.resolve() instanceof PsiLocalVariable) { + startOffset = referenceAt.getElement().getTextRange().getEndOffset(); + } else { + startOffset = editor.getDocument().getLineEndOffset(editor.getDocument().getLineNumber(startOffset)); + } + editor.getCaretModel().moveToOffset(startOffset); + } + exprMarker.dispose(); + } + }); } } } diff --git a/java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt b/java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt new file mode 100644 index 000000000000..3fc7c2fc04fd --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt @@ -0,0 +1,19 @@ +PsiJavaFile:ChainedClassObjectAccess.java + PsiReferenceExpression:A.class.B + PsiClassObjectAccessExpression:A.class + PsiTypeElement:A + PsiJavaCodeReferenceElement:A + PsiIdentifier:A('A') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiKeyword:class('class') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:B('B') + PsiJavaToken:DOT('.') + PsiErrorElement:Identifier expected + + PsiErrorElement:Unparsed tokens + PsiKeyword:class('class') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index db3af8cac07c..c5fb275f7855 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -105,6 +105,7 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testQualifiedSuperMethodCall1() { doParserTest("d.super(0)"); } public void testSuperMethodCallTypeParameterList() { doParserTest("super()"); } public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } + public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } private void doParserTest(final String text) { doParserTest(text, new TestParser() { diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java index e19addbcf2cb..bebca173a534 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java @@ -176,4 +176,102 @@ public class JavaFormatterSpaceTest extends AbstractJavaFormatterTest { "int[] i2 = new int[]{1}" ); } + + public void testSpaceBeforeElse() throws Exception { + // Inspired by IDEA-58068 + getSettings().ELSE_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_ELSE_KEYWORD = false; + doMethodTest( + "if (true) {\n" + + "} else {\n" + + "}", + "if (true) {\n" + + "}else {\n" + + "}" + ); + + getSettings().SPACE_BEFORE_ELSE_KEYWORD = true; + doMethodTest( + "if (true) {\n" + + "}else {\n" + + "}", + "if (true) {\n" + + "} else {\n" + + "}" + ); + } + + public void testSpaceBeforeWhile() throws Exception { + // Inspired by IDEA-58068 + getSettings().WHILE_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_WHILE_KEYWORD = false; + doMethodTest( + "do {\n" + + "} while (true);", + "do {\n" + + "}while (true);" + ); + + getSettings().SPACE_BEFORE_WHILE_KEYWORD = true; + doMethodTest( + "do {\n" + + "}while (true);", + "do {\n" + + "} while (true);" + ); + } + + + public void testSpaceBeforeCatch() throws Exception { + // Inspired by IDEA-58068 + getSettings().CATCH_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_CATCH_KEYWORD = false; + doMethodTest( + "try {\n" + + "} catch (Exception e) {\n" + + "}", + "try {\n" + + "}catch (Exception e) {\n" + + "}" + ); + + getSettings().SPACE_BEFORE_CATCH_KEYWORD = true; + doMethodTest( + "try {\n" + + "}catch (Exception e) {\n" + + "}", + "try {\n" + + "} catch (Exception e) {\n" + + "}" + ); + } + + public void testSpaceBeforeFinally() throws Exception { + // Inspired by IDEA-58068 + getSettings().FINALLY_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_FINALLY_KEYWORD = false; + doMethodTest( + "try {\n" + + "} finally {\n" + + "}", + "try {\n" + + "}finally {\n" + + "}" + ); + + getSettings().SPACE_BEFORE_FINALLY_KEYWORD = true; + doMethodTest( + "try {\n" + + "}finally {\n" + + "}", + "try {\n" + + "} finally {\n" + + "}" + ); + } + } diff --git a/java/openapi/src/com/intellij/lexer/JavaDocLexer.java b/java/openapi/src/com/intellij/lexer/JavaDocLexer.java index 355e2432737b..6efb4ab16cce 100644 --- a/java/openapi/src/com/intellij/lexer/JavaDocLexer.java +++ b/java/openapi/src/com/intellij/lexer/JavaDocLexer.java @@ -20,6 +20,6 @@ package com.intellij.lexer; */ public class JavaDocLexer extends DocCommentLexer { public JavaDocLexer(final boolean isJdk15Enabled) { - super(new JavaDocTokenTypes(), isJdk15Enabled); + super(JavaDocTokenTypes.INSTANCE, isJdk15Enabled); } } diff --git a/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java b/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java index 732a16bba737..b99d7da89bd6 100644 --- a/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java +++ b/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java @@ -22,6 +22,10 @@ import com.intellij.psi.tree.IElementType; * @author yole */ public class JavaDocTokenTypes implements DocCommentTokenTypes { + public static final DocCommentTokenTypes INSTANCE = new JavaDocTokenTypes(); + + private JavaDocTokenTypes() { } + public IElementType commentStart() { return JavaDocTokenType.DOC_COMMENT_START; } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java index a565eeecfa22..e5862741a6f6 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java @@ -49,7 +49,7 @@ public interface CodeStyleSettingsCustomizable { String WRAPPING_THROWS_KEYWORD = ApplicationBundle.message("wrapping.throws.keyword"); String WRAPPING_TERNARY_OPERATION = ApplicationBundle.message("wrapping.ternary.operation"); String WRAPPING_ASSIGNMENT = ApplicationBundle.message("wrapping.assignment.statement"); - String WRAPPING_FIELDS_VARIABLES_GROUPS = ApplicationBundle.message("wrapping.assignment.variables.groups"); + String WRAPPING_FIELDS_VARIABLES_GROUPS = ApplicationBundle.message("checkbox.align.multiline.fields.groups"); String WRAPPING_ARRAY_INITIALIZER = ApplicationBundle.message("wrapping.array.initializer"); String WRAPPING_MODIFIER_LIST = ApplicationBundle.message("wrapping.modifier.list"); String WRAPPING_ASSERT_STATEMENT = ApplicationBundle.message("wrapping.assert.statement"); diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java index eba4b435ffdc..698cd3ad5a7f 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java @@ -618,8 +618,6 @@ public class CommonCodeStyleSettings { //----------------- WRAPPING --------------------------- - public boolean WRAP_WHEN_TYPING_REACHES_RIGHT_MAGIN = false; - public static final int DO_NOT_WRAP = 0x00; public static final int WRAP_AS_NEEDED = 0x01; public static final int WRAP_ALWAYS = 0x02; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java index dddcb961b6d8..a9325e08b8dc 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java @@ -90,7 +90,7 @@ public class StatusBarUpdater implements Disposable { if (statusBar instanceof StatusBarEx) { StatusBarEx barEx = (StatusBarEx)statusBar; if (!text.equals(barEx.getInfo())){ - statusBar.setInfo(text); + statusBar.setInfo(text, "updater"); } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java index 62ba53c03c45..232db7d9ee4f 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java @@ -28,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.problems.WolfTheProblemSolver; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.ui.docking.DockManager; import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -47,8 +48,8 @@ public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { private final MyPsiTreeChangeListener myPsiTreeChangeListener; private final WolfTheProblemSolver.ProblemListener myProblemListener; - public PsiAwareFileEditorManagerImpl(final Project project, final PsiManager psiManager, final WolfTheProblemSolver problemSolver) { - super(project); + public PsiAwareFileEditorManagerImpl(final Project project, final PsiManager psiManager, final WolfTheProblemSolver problemSolver, DockManager dockManager) { + super(project, dockManager); myPsiManager = psiManager; myProblemSolver = problemSolver; myPsiTreeChangeListener = new MyPsiTreeChangeListener(); diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java index 6cc9cfe1281b..f03ed1b19187 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java @@ -59,6 +59,8 @@ import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.TextOccurrencesUtil; import com.intellij.usageView.UsageInfo; +import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.PairProcessor; import com.intellij.util.containers.MultiMap; import com.intellij.util.containers.Stack; @@ -99,7 +101,7 @@ public class VariableInplaceRenamer { return performInplaceRename(true, null, null); } - public boolean performInplaceRename(boolean processTextOccurrences, LinkedHashSet nameSuggestions, final RangeMarker offsetAfter) { + public boolean performInplaceRename(boolean processTextOccurrences, LinkedHashSet nameSuggestions, final Consumer moveOffsetAfterr) { if (InjectedLanguageUtil.isInInjectedLanguagePrefixSuffix(myElementToRename)) { return false; } @@ -217,21 +219,15 @@ public class VariableInplaceRenamer { if (myNewName != null) { performAutomaticRename(myNewName, PsiTreeUtil.getParentOfType(containingFile.findElementAt(renameOffset), PsiNameIdentifierOwner.class)); } - if (offsetAfter != null) { - int startOffset = offsetAfter.getStartOffset(); - final PsiReference referenceAt = containingFile.findReferenceAt(startOffset); - if (referenceAt != null) { - startOffset = referenceAt.getElement().getTextRange().getEndOffset(); - } - myEditor.getCaretModel().moveToOffset(startOffset); - offsetAfter.dispose(); + if (moveOffsetAfterr != null) { + moveOffsetAfterr.consume(true); } } public void templateCancelled(Template template) { finish(); - if (offsetAfter != null) { - offsetAfter.dispose(); + if (moveOffsetAfterr != null) { + moveOffsetAfterr.consume(false); } } }); diff --git a/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java b/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java index 5d9f169dda42..20666f5013c9 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java +++ b/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java @@ -60,6 +60,10 @@ public class Registry { return get(key).asInteger(); } + public static double doubleValue(@PropertyKey(resourceBundle = REGISTRY_BUNDLE) String key) { + return get(key).asDouble(); + } + public static String stringValue(@PropertyKey(resourceBundle = REGISTRY_BUNDLE) String key) { return get(key).asString(); } diff --git a/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java b/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java index b791891f4b69..f653548634bf 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java +++ b/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java @@ -37,6 +37,7 @@ public class RegistryValue { private String myStringCachedValue; private Integer myIntCachedValue; + private Double myDoubleCachedValue; private Boolean myBooleanCachedValue; RegistryValue(Registry registry, String key) { @@ -71,6 +72,14 @@ public class RegistryValue { return myIntCachedValue.intValue(); } + public double asDouble() { + if (myDoubleCachedValue == null) { + myDoubleCachedValue = Double.valueOf(get(myKey, "0.0", true)); + } + + return myDoubleCachedValue.doubleValue(); + } + public Color asColor(Color defaultValue) { final String s = get(myKey, null, true); if (s != null) { diff --git a/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java b/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java index 52df1fda6960..6e348a398ebd 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java @@ -131,8 +131,7 @@ public abstract class IdeFocusManager implements FocusRequestor { return fm; } - public Component getFocusOwner() { - return isFocusBeingTransferred() ? null : KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - } + public abstract Component getFocusOwner(); + public abstract void runOnOwnContext(DataContext context, Runnable runnable); } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java b/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java index eb96491daae1..2bcfd182d812 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; import org.jetbrains.annotations.NotNull; @@ -86,6 +87,16 @@ public class PassThroughtIdeFocusManager extends IdeFocusManager { return this; } + @Override + public Component getFocusOwner() { + return KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + runnable.run(); + } + @Override public void suspendKeyProcessingUntil(@NotNull ActionCallback done) { } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java index d2697cb7331a..b774a96dec08 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java @@ -39,12 +39,16 @@ public interface StatusBar extends StatusBarInfo { } public static void set(@Nullable final String text, @Nullable final Project project) { + set(text, project, null); + } + + public static void set(@Nullable final String text, @Nullable final Project project, @Nullable final String requestor) { if (project != null) { if (project.isDisposed()) return; if (!project.isInitialized()) { StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { public void run() { - project.getMessageBus().syncPublisher(TOPIC).setInfo(text); + project.getMessageBus().syncPublisher(TOPIC).setInfo(text, requestor); } }); return; @@ -52,7 +56,7 @@ public interface StatusBar extends StatusBarInfo { } final MessageBus bus = project == null ? ApplicationManager.getApplication().getMessageBus() : project.getMessageBus(); - bus.syncPublisher(TOPIC).setInfo(text); + bus.syncPublisher(TOPIC).setInfo(text, requestor); } } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java index 78d2ecc3cab9..7c4c371db1a5 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java @@ -27,6 +27,8 @@ public interface StatusBarInfo { * @param s text to be shown in the status bar */ void setInfo(@Nullable String s); + + void setInfo(@Nullable String s, @Nullable String requestor); String getInfo(); } diff --git a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java index e15e44b177c0..a444ca5e8e8c 100644 --- a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java @@ -45,6 +45,8 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe private final Disposable myParentDisposable; private Dimension myDelta; + private boolean myDetachingMode; + public MouseDragHelper(Disposable parent, final JComponent dragComponent) { myDragComponent = dragComponent; myParentDisposable = parent; @@ -111,12 +113,17 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe if (wasDragging) { try { - processDragFinish(e); + if (myDetachingMode) { + processDragOutFinish(e); + } else { + processDragFinish(e, false); + } } finally { myDraggingNow = false; myPressPointComponent = null; myPressPointScreen = null; + myDetachingMode = false; e.consume(); } } @@ -141,7 +148,20 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe draggedTo.y -= myDelta.height; - processDrag(e, draggedTo, (Point)myPressPointScreen.clone()); + boolean dragOutStarted = false; + if (!myDetachingMode) { + if (isDragOut(e, draggedTo, (Point)myPressPointScreen.clone())) { + myDetachingMode = true; + processDragFinish(e, true); + dragOutStarted = true; + } + } + + if (myDetachingMode) { + processDragOut(e, draggedTo, (Point)myPressPointScreen.clone(), dragOutStarted); + } else { + processDrag(e, draggedTo, (Point)myPressPointScreen.clone()); + } e.consume(); } @@ -161,7 +181,11 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe return true; } - protected void processDragFinish(final MouseEvent even) { + + protected void processDragFinish(final MouseEvent event, boolean willDragOutStart) { + } + + protected void processDragOutFinish(final MouseEvent event) { } public final boolean isDragJustStarted() { @@ -170,6 +194,13 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe protected abstract void processDrag(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint); + protected boolean isDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint) { + return false; + } + + protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) { + + } private boolean isWithinDeadZone(final MouseEvent e) { final Point screen = new RelativePoint(e).getScreenPoint(); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java new file mode 100644 index 000000000000..3b0b0309efe1 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2010 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.ui.docking; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.awt.RelativeRectangle; +import com.intellij.util.ui.update.Activatable; +import org.jdom.Element; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +public interface DockContainer extends Disposable, Activatable { + + RelativeRectangle getAcceptArea(); + boolean canAccept(DockableContent content, RelativePoint point); + + JComponent getComponent(); + + void add(DockableContent content, RelativePoint dropTarget); + + void closeAll(); + + void addListener(Listener listener, Disposable parent); + + boolean isEmpty(); + + @Nullable + Image startDropOver(DockableContent content, RelativePoint point); + + @Nullable + Image processDropOver(DockableContent content, RelativePoint point); + + @Nullable + void resetDropOver(DockableContent content); + + + boolean isDisposeWhenEmpty(); + + interface Persistent extends DockContainer { + + String getDockContainerType(); + Element getState(); + + } + + interface Listener { + void contentAdded(Object key); + void contentRemoved(Object key); + + class Adapter implements Listener { + @Override + public void contentAdded(Object key) { + } + + @Override + public void contentRemoved(Object key) { + } + } + } +} diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java new file mode 100644 index 000000000000..3dcc059e3de2 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2010 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.ui.docking; + +import com.intellij.openapi.Disposable; +import org.jdom.Element; + +public interface DockContainerFactory extends Disposable { + + DockContainer createContainer(); + + interface Persistent extends DockContainerFactory { + + DockContainer loadContainerFrom(Element element); + + } + +} diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java new file mode 100644 index 000000000000..2aac3d3c71db --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2010 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.ui.docking; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; + +import java.awt.event.MouseEvent; +import java.util.Set; + +public abstract class DockManager implements ProjectComponent { + + public abstract void register(DockContainer container); + public abstract void register(String id, DockContainerFactory factory); + + public static DockManager getInstance(Project project) { + return project.getComponent(DockManager.class); + } + + public abstract DragSession createDragSession(MouseEvent mouseEvent, DockableContent content); + + public abstract Set getContainers(); +} diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java new file mode 100644 index 000000000000..e4981fb47eb4 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2010 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.ui.docking; + +import com.intellij.openapi.actionSystem.Presentation; + +import java.awt.*; + +public interface DockableContent { + + T getKey(); + + Image getPreviewImage(); + + String getDockContainerType(); + + Dimension getPreferredSize(); + + void close(); + + Presentation getPresentation(); +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java b/platform/platform-api/src/com/intellij/ui/docking/DragSession.java similarity index 67% rename from platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java rename to platform/platform-api/src/com/intellij/ui/docking/DragSession.java index 5594bcd23012..1704cebf8574 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DragSession.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.vcs.annotate; +package com.intellij.ui.docking; + +import java.awt.event.MouseEvent; + +public interface DragSession { + + void process(MouseEvent e); -/** - * Markup interface for aspects always visible in the gutter. Other aspects will be visible only after - * Show Details action - * - * @author Konstantin Bulenkov - */ -public interface MajorLineAnnotationAspect extends LineAnnotationAspect { } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java index e5f352b4d1e7..cc602f5c00f0 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java @@ -19,6 +19,7 @@ import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Getter; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; import org.jetbrains.annotations.NotNull; @@ -116,4 +117,12 @@ public interface JBTabs extends SwitchProvider { boolean isDisposed(); JBTabs setAdditinalSwitchProviderWhenOriginal(SwitchProvider delegate); + + Image getComponentImage(TabInfo info); + + void resetDropOver(TabInfo tabInfo); + Image startDropOver(TabInfo tabInfo, RelativePoint point); + void processDropOver(TabInfo over, RelativePoint point); + + boolean isEmptyVisible(); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java index 63eeb5c50303..ec5e96c72169 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeSupport; import java.lang.ref.WeakReference; import java.util.Map; @@ -81,6 +82,7 @@ public final class TabInfo implements Queryable { private Color myTabColor = null; private Queryable myQueryable; + private DragOutDelegate myDragOutDelegate; public TabInfo(final JComponent component) { myComponent = component; @@ -353,4 +355,25 @@ public final class TabInfo implements Queryable { myQueryable.putInfo(info); } } + + public TabInfo setDragOutDelegate(DragOutDelegate delegate) { + myDragOutDelegate = delegate; + return this; + } + + public boolean canBeDraggedOut() { + return myDragOutDelegate != null; + } + + public DragOutDelegate getDragOutDelegate() { + return myDragOutDelegate; + } + + public interface DragOutDelegate { + + void dragOutStarted(MouseEvent mouseEvent, TabInfo info); + void processDragOut(MouseEvent event, TabInfo source); + void dragOutFinished(MouseEvent event, TabInfo source); + } + } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java index 9b22e29565ae..e999913a23e9 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java @@ -37,11 +37,40 @@ class DragHelper extends MouseDragHelper { Measurer myHorizontal = new Measurer.Width(); Measurer myVertical = new Measurer.Height(); + private TabInfo myDragOutSource; + public DragHelper(JBTabsImpl tabs) { super(tabs, tabs); myTabs = tabs; } + + @Override + protected boolean isDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint) { + if (myDragSource == null || !myDragSource.canBeDraggedOut()) return false; + + TabLabel label = myTabs.myInfo2Label.get(myDragSource); + + return myTabs.getEffectiveLayout().isDragOut(label, dragToScreenPoint.x - startScreenPoint.x, dragToScreenPoint.y - startScreenPoint.y); + } + + @Override + protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) { + TabInfo.DragOutDelegate delegate = myDragOutSource.getDragOutDelegate(); + if (justStarted) { + delegate.dragOutStarted(event, myDragOutSource); + } else { + delegate.processDragOut(event, myDragOutSource); + } + } + + @Override + protected void processDragOutFinish(MouseEvent event) { + super.processDragOutFinish(event); + + myDragOutSource.getDragOutDelegate().dragOutFinished(event, myDragSource); + } + protected void processDrag(MouseEvent event, Point targetScreenPoint, Point startPointScreen) { if (!myTabs.isTabDraggingEnabled()) return; @@ -204,11 +233,19 @@ class DragHelper extends MouseDragHelper { } @Override - protected void processDragFinish(MouseEvent even) { + protected void processDragFinish(MouseEvent event, boolean willDragOutStart) { + super.processDragFinish(event, willDragOutStart); + + if (willDragOutStart) { + myDragOutSource = myDragSource; + } + myDragSource = null; myDragRec = null; myTabs.resetTabsCache(); myTabs.relayout(true, false); + + myTabs.revalidate(); } } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 3df5f186e975..8e1a78aeb309 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -29,6 +29,7 @@ import com.intellij.openapi.wm.IdeGlassPane; import com.intellij.openapi.wm.IdeGlassPaneUtil; import com.intellij.openapi.wm.impl.content.GraphicsConfig; import com.intellij.ui.CaptionPanel; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.switcher.QuickActionProvider; import com.intellij.ui.switcher.SwitchProvider; @@ -66,7 +67,7 @@ public class JBTabsImpl extends JComponent static DataKey NAVIGATION_ACTIONS_KEY = DataKey.create("JBTabs"); ActionManager myActionManager; - public final List myVisibleInfos = new ArrayList(); + private final List myVisibleInfos = new ArrayList(); private final Map myHiddenInfos = new HashMap(); private TabInfo mySelectedInfo; @@ -159,6 +160,8 @@ public class JBTabsImpl extends JComponent private boolean myOwnSwitchProvider = true; private SwitchProvider mySwitchDelegate; + private TabInfo myDropInfo; + private int myDropInfoIndex; public JBTabsImpl(@NotNull Project project) { this(project, project); @@ -297,6 +300,21 @@ public class JBTabsImpl extends JComponent return this; } + @Override + public Image getComponentImage(TabInfo info) { + JComponent cmp = info.getComponent(); + + BufferedImage img; + if (cmp.isShowing()) { + img = new BufferedImage(cmp.getWidth(), cmp.getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + cmp.paint(g); + } else { + img = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB); + } + return img; + } + public void dispose() { myDisposed = true; mySelectedInfo = null; @@ -394,6 +412,14 @@ public class JBTabsImpl extends JComponent } } + public boolean isDropTarget(TabInfo info) { + return myDropInfo != null && myDropInfo == info; + } + + public void setDropInfoIndex(int dropInfoIndex) { + myDropInfoIndex = dropInfoIndex; + } + class TabActionsAutoHideListener extends MouseMotionAdapter { private TabLabel myCurrentOverLabel; @@ -555,7 +581,11 @@ public class JBTabsImpl extends JComponent @NotNull public TabInfo addTab(TabInfo info, int index) { - if (getTabs().contains(info)) { + return addTab(info, index, false); + } + + private TabInfo addTab(TabInfo info, int index, boolean isDropTarget) { + if (!isDropTarget && getTabs().contains(info)) { return getTabs().get(getTabs().indexOf(info)); } @@ -563,14 +593,16 @@ public class JBTabsImpl extends JComponent final TabLabel label = new TabLabel(this, info); myInfo2Label.put(info, label); - if (index < 0) { - myVisibleInfos.add(info); - } - else if (index > myVisibleInfos.size() - 1) { - myVisibleInfos.add(info); - } - else { - myVisibleInfos.add(index, info); + if (!isDropTarget) { + if (index < 0) { + myVisibleInfos.add(info); + } + else if (index > myVisibleInfos.size() - 1) { + myVisibleInfos.add(info); + } + else { + myVisibleInfos.add(index, info); + } } resetTabsCache(); @@ -591,9 +623,11 @@ public class JBTabsImpl extends JComponent updateHiding(); } - if (getTabCount() == 1) { - fireBeforeSelectionChanged(null, info); - fireSelectionChanged(null, info); + if (!isDropTarget) { + if (getTabCount() == 1) { + fireBeforeSelectionChanged(null, info); + fireSelectionChanged(null, info); + } } return info; @@ -1195,12 +1229,23 @@ public class JBTabsImpl extends JComponent } + List visible = new ArrayList(); + visible.addAll(myVisibleInfos); + + if (myDropInfo != null && !visible.contains(myDropInfo)) { + if (getDropInfoIndex() >= 0 && getDropInfoIndex() < visible.size()) { + visible.add(getDropInfoIndex(), myDropInfo); + } else { + visible.add(myDropInfo); + } + } + if (isSingleRow()) { - myLastLayoutPass = mySingleRowLayout.layoutSingleRow(); + myLastLayoutPass = mySingleRowLayout.layoutSingleRow(visible); myTableLayout.myLastTableLayout = null; } else { - myLastLayoutPass = myTableLayout.layoutTable(); + myLastLayoutPass = myTableLayout.layoutTable(visible); mySingleRowLayout.myLastSingRowLayout = null; } @@ -1319,6 +1364,10 @@ public class JBTabsImpl extends JComponent reset(each, resetLabels); } + if (myDropInfo != null) { + reset(myDropInfo, resetLabels); + } + for (TabInfo each : myHiddenInfos.keySet()) { reset(each, resetLabels); } @@ -1697,6 +1746,8 @@ public class JBTabsImpl extends JComponent } private void paintNonSelected(final Graphics2D g2d, final TabInfo each, final boolean leftGhostExists, final boolean rightGhostExists) { + if (myDropInfo == each) return; + final TabLabel label = myInfo2Label.get(each); if (label.getBounds().width == 0) return; @@ -2098,7 +2149,17 @@ public class JBTabsImpl extends JComponent } public ActionCallback removeTab(final TabInfo info, @Nullable TabInfo forcedSelectionTranfer, boolean transferFocus) { - if (info == null || !getTabs().contains(info)) return new ActionCallback.Done(); + return removeTab(info, forcedSelectionTranfer, transferFocus, false); + } + + private ActionCallback removeTab(TabInfo info, TabInfo forcedSelectionTranfer, boolean transferFocus, boolean isDropTarget) { + if (!isDropTarget) { + if (info == null || !getTabs().contains(info)) return new ActionCallback.Done(); + } + + if (isDropTarget && myLastLayoutPass != null) { + myLastLayoutPass.myVisibleInfos.remove(info); + } final ActionCallback result = new ActionCallback(); @@ -2593,7 +2654,7 @@ public class JBTabsImpl extends JComponent return !myHorizontalSide; } - private TabLayout getEffectiveLayout() { + public TabLayout getEffectiveLayout() { if (myLayout == myTableLayout && getTabsPosition() == JBTabsPosition.top) return myTableLayout; return mySingleRowLayout; } @@ -2905,4 +2966,54 @@ public class JBTabsImpl extends JComponent return new Object[] {myInfo}; } } + + @Override + public void resetDropOver(TabInfo tabInfo) { + if (myDropInfo != null) { + TabInfo dropInfo = myDropInfo; + myDropInfo = null; + setDropInfoIndex(-1); + removeTab(dropInfo, null, false, true); + } + } + + @Override + public Image startDropOver(TabInfo tabInfo, RelativePoint point) { + myDropInfo = tabInfo; + + int index = myLayout.getDropIndexFor(point.getPoint(this)); + setDropInfoIndex(index); + addTab(myDropInfo, index, true); + + TabLabel label = myInfo2Label.get(myDropInfo); + Dimension size = label.getPreferredSize(); + label.setBounds(0, 0, size.width, size.height); + + BufferedImage img = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = img.createGraphics(); + label.paintOffscreen(g); + g.dispose(); + + relayout(true, false); + + return img; + } + + @Override + public void processDropOver(TabInfo over, RelativePoint point) { + int index = myLayout.getDropIndexFor(point.getPoint(this)); + if (index != getDropInfoIndex()) { + setDropInfoIndex(index); + relayout(true, false); + } + } + + public int getDropInfoIndex() { + return myDropInfoIndex; + } + + @Override + public boolean isEmptyVisible() { + return myVisibleInfos.isEmpty(); + } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java index c8e2bc301e26..904a78f463d3 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java @@ -21,6 +21,13 @@ import java.awt.*; import java.util.List; public abstract class LayoutPassInfo { + + public java.util.List myVisibleInfos; + + protected LayoutPassInfo(List visibleInfos) { + myVisibleInfos = visibleInfos; + } + public abstract TabInfo getPreviousFor(TabInfo info); public abstract TabInfo getNextFor(TabInfo info); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java index d027ace3397b..745a84d233e8 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java @@ -110,7 +110,14 @@ public class TabLabel extends JPanel { myCentered = toCenter; } + public void paintOffscreen(Graphics g) { + validateTree(); + doPaint(g); + } + public void paint(final Graphics g) { + if (myTabs.isDropTarget(myInfo)) return; + if (myTabs.getSelectedInfo() != myInfo) { myImage = null; doPaint(g); @@ -167,18 +174,22 @@ public class TabLabel extends JPanel { break; } - if (myTabs.getSelectedInfo() != myInfo) { - g.translate(dX, dY); - } else { - g.translate(dXs, dYs); + if (!myTabs.isDropTarget(myInfo)) { + if (myTabs.getSelectedInfo() != myInfo) { + g.translate(dX, dY); + } else { + g.translate(dXs, dYs); + } } super.paint(g); - if (myTabs.getSelectedInfo() != myInfo) { - g.translate(-dX, -dY); - } else { - g.translate(-dXs, -dYs); + if (!myTabs.isDropTarget(myInfo)) { + if (myTabs.getSelectedInfo() != myInfo) { + g.translate(-dX, -dY); + } else { + g.translate(-dXs, -dYs); + } } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java index 8022a4457d0a..8a452c998e70 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java @@ -15,6 +15,9 @@ */ package com.intellij.ui.tabs.impl; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.ui.awt.RelativePoint; + import java.awt.*; public abstract class TabLayout { @@ -28,8 +31,17 @@ public abstract class TabLayout { return new ShapeTransform.Top(rectangle); } + public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) { + return Math.abs(deltaY) > tabLabel.getSize().height * getDragOutMultiplier(); + } + public boolean isSideComponentOnTabs() { return false; } + public static double getDragOutMultiplier() { + return Registry.doubleValue("ide.tabbedPane.dragOutMultiplier"); + } + + public abstract int getDropIndexFor(Point point); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java index 876b06f794e2..8255e217f2cd 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java @@ -15,9 +15,9 @@ */ package com.intellij.ui.tabs.impl.singleRow; +import com.intellij.ui.tabs.JBTabs; import com.intellij.ui.tabs.TabInfo; import com.intellij.ui.tabs.impl.*; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -66,6 +66,11 @@ public class SingleRowLayout extends TabLayout { return getStrategy().createShapeTransform(labelRec); } + @Override + public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) { + return getStrategy().isDragOut(tabLabel, deltaX, deltaY); + } + public SingleRowLayout(final JBTabsImpl tabs) { myTabs = tabs; myTop = new SingleRowLayoutStrategy.Top(this); @@ -89,14 +94,14 @@ public class SingleRowLayout extends TabLayout { return null; } - private boolean checkLayoutLabels() { + private boolean checkLayoutLabels(SingleRowPassInfo data) { boolean layoutLabels = true; if (!myTabs.myForcedRelayout && myLastSingRowLayout != null && myLastSingRowLayout.contentCount == myTabs.getTabCount() && myLastSingRowLayout.laayoutSize.equals(myTabs.getSize())) { - for (TabInfo each : myTabs.myVisibleInfos) { + for (TabInfo each : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(each); if (!eachLabel.isValid()) { break; @@ -112,12 +117,12 @@ public class SingleRowLayout extends TabLayout { return layoutLabels; } - public LayoutPassInfo layoutSingleRow() { - SingleRowPassInfo data = new SingleRowPassInfo(this); + public LayoutPassInfo layoutSingleRow(java.util.List visibleInfos) { + SingleRowPassInfo data = new SingleRowPassInfo(this, visibleInfos); final TabInfo selected = myTabs.getSelectedInfo(); final JBTabsImpl.Toolbar selectedToolbar = myTabs.myInfo2Toolbar.get(selected); - final boolean layoutLabels = checkLayoutLabels(); + final boolean layoutLabels = checkLayoutLabels(data); if (!layoutLabels) { data = myLastSingRowLayout; } @@ -148,10 +153,10 @@ public class SingleRowLayout extends TabLayout { getStrategy().layoutComp(data); } - if (data.toLayout.size() > 0 && myTabs.myVisibleInfos.size() > 0) { - final int left = myTabs.myVisibleInfos.indexOf(data.toLayout.get(0)); - final int right = myTabs.myVisibleInfos.indexOf(data.toLayout.get(data.toLayout.size() - 1)); - myMoreIcon.setPaintedIcons(left > 0, right < myTabs.myVisibleInfos.size() - 1); + if (data.toLayout.size() > 0 && data.myVisibleInfos.size() > 0) { + final int left = data.myVisibleInfos.indexOf(data.toLayout.get(0)); + final int right = data.myVisibleInfos.indexOf(data.toLayout.get(data.toLayout.size() - 1)); + myMoreIcon.setPaintedIcons(left > 0, right < data.myVisibleInfos.size() - 1); } else { myMoreIcon.setPaintedIcons(false, false); @@ -236,7 +241,7 @@ public class SingleRowLayout extends TabLayout { data.toFitLength -= myTabs.getGhostTabLength() * 2; } - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { data.requiredLength += getStrategy().getLengthIncrement(myTabs.myInfo2Label.get(eachInfo).getPreferredSize()); data.toLayout.add(eachInfo); } @@ -273,10 +278,10 @@ public class SingleRowLayout extends TabLayout { } } - for (int i = 1; i < myTabs.myVisibleInfos.size() - 1; i++) { - final TabInfo each = myTabs.myVisibleInfos.get(i); - final TabInfo prev = myTabs.myVisibleInfos.get(i - 1); - final TabInfo next = myTabs.myVisibleInfos.get(i + 1); + for (int i = 1; i < data.myVisibleInfos.size() - 1; i++) { + final TabInfo each = data.myVisibleInfos.get(i); + final TabInfo prev = data.myVisibleInfos.get(i - 1); + final TabInfo next = data.myVisibleInfos.get(i + 1); if (data.toLayout.contains(each) && data.toDrop.contains(prev)) { myLeftGhost.setInfo(prev); @@ -340,4 +345,77 @@ public class SingleRowLayout extends TabLayout { } } } + + @Override + public int getDropIndexFor(Point point) { + if (myLastSingRowLayout == null) return -1; + + int result = -1; + + Component c = myTabs.getComponentAt(point); + + if (c instanceof JBTabsImpl && !(c instanceof TabLabel)) { + for (int i = 0; i < myLastSingRowLayout.myVisibleInfos.size() - 1; i++) { + TabLabel first = myTabs.myInfo2Label.get(myLastSingRowLayout.myVisibleInfos.get(i)); + TabLabel second = myTabs.myInfo2Label.get(myLastSingRowLayout.myVisibleInfos.get(i + 1)); + + Rectangle firstBounds = first.getBounds(); + Rectangle secondBounds = second.getBounds(); + + boolean between = false; + + boolean horizontal = getStrategy() instanceof SingleRowLayoutStrategy.Horizontal; + if (horizontal) { + between = firstBounds.getMaxX() < point.x + && secondBounds.getX() > point.x + && firstBounds.y < point.y + && secondBounds.getMaxY() > point.y; + } else { + between = firstBounds.getMaxY() < point.y + && secondBounds.getY() > point.y + && firstBounds.x < point.x + && secondBounds.getMaxX() > point.x; + } + + if (between) { + c = first; + break; + } + } + + } + + if (c instanceof TabLabel) { + TabInfo info = ((TabLabel)c).getInfo(); + int index = myLastSingRowLayout.myVisibleInfos.indexOf(info); + boolean isDropTarget = myTabs.isDropTarget(info); + if (!isDropTarget) { + for (int i = 0; i <= index; i++) { + if (myTabs.isDropTarget(myLastSingRowLayout.myVisibleInfos.get(i))) { + index -= 1; + break; + } + } + result = index; + } else if (index < myLastSingRowLayout.myVisibleInfos.size()) { + result = index; + } + } else if (c instanceof GhostComponent) { + GhostComponent ghost = (GhostComponent)c; + TabInfo info = ghost.myInfo; + if (info != null) { + int index = myLastSingRowLayout.myVisibleInfos.indexOf(info); + index += myLeftGhost == ghost ? -1 : 1; + result = index >= 0 && index < myLastSingRowLayout.myVisibleInfos.size() ? index : -1; + } else { + if (myLastSingRowLayout.myVisibleInfos.size() == 0) { + result = 0; + } else { + result = myLeftGhost == ghost ? 0 : myLastSingRowLayout.myVisibleInfos.size() - 1; + } + } + } + + return result; + } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java index 7b7810998a0e..2ebaa704d5ed 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java @@ -17,6 +17,8 @@ package com.intellij.ui.tabs.impl.singleRow; import com.intellij.ui.tabs.impl.JBTabsImpl; import com.intellij.ui.tabs.impl.ShapeTransform; +import com.intellij.ui.tabs.impl.TabLabel; +import com.intellij.ui.tabs.impl.table.TableLayout; import java.awt.*; @@ -62,6 +64,8 @@ public abstract class SingleRowLayoutStrategy { return false; } + public abstract boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY); + abstract static class Horizontal extends SingleRowLayoutStrategy { protected Horizontal(final SingleRowLayout layout) { super(layout); @@ -76,6 +80,11 @@ public abstract class SingleRowLayoutStrategy { return true; } + @Override + public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) { + return Math.abs(deltaY) > tabLabel.getHeight() * TableLayout.getDragOutMultiplier(); + } + public int getMoreRectAxisSize() { return myLayout.myMoreIcon.getIconWidth() + 6; } @@ -204,6 +213,11 @@ public abstract class SingleRowLayoutStrategy { super(layout); } + @Override + public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) { + return Math.abs(deltaX) > tabLabel.getHeight() * TableLayout.getDragOutMultiplier(); + } + public boolean isToCenterTextWhenStretched() { return false; } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java index 87b3dd61e9de..bbc9057dccb6 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java @@ -51,7 +51,9 @@ public class SingleRowPassInfo extends LayoutPassInfo { public JComponent comp; public Rectangle tabRectangle; - public SingleRowPassInfo(SingleRowLayout layout) { + + public SingleRowPassInfo(SingleRowLayout layout, java.util.List visibleInfos) { + super(visibleInfos); myTabs = layout.myTabs; laayoutSize = layout.myTabs.getSize(); contentCount = myTabs.getTabCount(); @@ -61,11 +63,11 @@ public class SingleRowPassInfo extends LayoutPassInfo { } public TabInfo getPreviousFor(final TabInfo info) { - return getPrevious(myTabs.myVisibleInfos, myTabs.myVisibleInfos.indexOf(info)); + return getPrevious(myVisibleInfos, myVisibleInfos.indexOf(info)); } public TabInfo getNextFor(final TabInfo info) { - return getNext(myTabs.myVisibleInfos, myTabs.myVisibleInfos.indexOf(info)); + return getNext(myVisibleInfos, myVisibleInfos.indexOf(info)); } public int getRowCount() { @@ -73,11 +75,11 @@ public class SingleRowPassInfo extends LayoutPassInfo { } public int getColumnCount(final int row) { - return myTabs.myVisibleInfos.size(); + return myVisibleInfos.size(); } public TabInfo getTabAt(final int row, final int column) { - return myTabs.myVisibleInfos.get(column); + return myVisibleInfos.get(column); } public Rectangle getHeaderRectangle() { diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java index 2efd1ec0d6b3..0602d22eeb42 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java @@ -34,8 +34,8 @@ public class TableLayout extends TabLayout { myTabs = tabs; } - private TablePassInfo computeLayoutTable() { - final TablePassInfo data = new TablePassInfo(myTabs); + private TablePassInfo computeLayoutTable(java.util.List visibleInfos) { + final TablePassInfo data = new TablePassInfo(myTabs, visibleInfos); final Insets insets = myTabs.getLayoutInsets(); data.toFitRec = @@ -46,7 +46,7 @@ public class TableLayout extends TabLayout { data.requiredRows = 1; - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(eachInfo); final Dimension size = eachLabel.getPreferredSize(); if (eachX + size.width >= data.toFitRec.getMaxX()) { @@ -65,7 +65,7 @@ public class TableLayout extends TabLayout { if (data.requiredRows > 1) { final int rowFit = insets.left + data.requiredWidth / data.requiredRows; - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(eachInfo); final Rectangle eachBounds = eachLabel.getBounds(); if (eachBounds.contains(rowFit, 0)) { @@ -75,7 +75,7 @@ public class TableLayout extends TabLayout { } } - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(eachInfo); final Dimension size = eachLabel.getPreferredSize(); if (eachX + size.width <= data.rowToFitMaxX) { @@ -112,9 +112,9 @@ public class TableLayout extends TabLayout { } - public LayoutPassInfo layoutTable() { + public LayoutPassInfo layoutTable(java.util.List visibleInfos) { myTabs.resetLayout(true); - final TablePassInfo data = computeLayoutTable(); + final TablePassInfo data = computeLayoutTable(visibleInfos); final Insets insets = myTabs.getLayoutInsets(); int eachY = insets.top; int eachX; @@ -169,4 +169,8 @@ public class TableLayout extends TabLayout { return data; } + @Override + public int getDropIndexFor(Point point) { + return -1; + } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java index 176660a0daa1..0ce59e7855dc 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java @@ -36,7 +36,8 @@ public class TablePassInfo extends LayoutPassInfo { JBTabsImpl myTabs; - TablePassInfo(final JBTabsImpl tabs) { + TablePassInfo(final JBTabsImpl tabs, java.util.List visibleInfos) { + super(visibleInfos); myTabs = tabs; } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java index ba95dc4ac083..81d83dcc3ab5 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.EditorWindow; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -29,12 +30,14 @@ public class CloseEditorAction extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(PlatformDataKeys.PROJECT); - final FileEditorManagerEx editorManager = ((FileEditorManagerEx)FileEditorManager.getInstance(project)); + final FileEditorManagerImpl editorManager = getEditorManager(project); EditorWindow window = e.getData(EditorWindow.DATA_KEY); - VirtualFile file; + VirtualFile file = null; if (window == null) { - window = editorManager.getCurrentWindow(); - file = window.getSelectedFile(); + window = editorManager.getActiveWindow().getResult(); + if (window != null) { + file = window.getSelectedFile(); + } } else { file = e.getData(PlatformDataKeys.VIRTUAL_FILE); @@ -44,6 +47,10 @@ public class CloseEditorAction extends AnAction implements DumbAware { } } + private FileEditorManagerImpl getEditorManager(Project project) { + return ((FileEditorManagerImpl)FileEditorManager.getInstance(project)); + } + public void update(final AnActionEvent event){ final Presentation presentation = event.getPresentation(); final Project project = event.getData(PlatformDataKeys.PROJECT); @@ -56,7 +63,7 @@ public class CloseEditorAction extends AnAction implements DumbAware { } EditorWindow window = event.getData(EditorWindow.DATA_KEY); if (window == null) { - window = ((FileEditorManagerEx)FileEditorManager.getInstance(project)).getCurrentWindow(); + window = getEditorManager(project).getActiveWindow().getResult(); } presentation.setEnabled(window != null && window.getTabCount() > 0); } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java index 52e453b70a1c..c71097fa529e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java @@ -20,11 +20,14 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.ex.ActionUtil; +import com.intellij.openapi.actionSystem.ex.DataConstantsEx; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.SizedIcon; import com.intellij.ui.plaf.beg.BegMenuItemUI; import com.intellij.util.Icons; @@ -207,19 +210,24 @@ public class ActionMenuItem extends JMenuItem { } public void actionPerformed(final ActionEvent e) { - AnActionEvent event = new AnActionEvent( - new MouseEvent(ActionMenuItem.this, MouseEvent.MOUSE_PRESSED, 0, e.getModifiers(), getWidth() / 2, getHeight() / 2, 1, false), - myContext, myPlace, myPresentation, ActionManager.getInstance(), e.getModifiers()); - if (ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) { - ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); - actionManager.fireBeforeActionPerformed(myAction, myContext, event); - Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(event.getDataContext()); - if (component != null && !isInTree(component)) { - return; + IdeFocusManager.findInstanceByContext(myContext).runOnOwnContext(myContext, new Runnable() { + @Override + public void run() { + AnActionEvent event = new AnActionEvent( + new MouseEvent(ActionMenuItem.this, MouseEvent.MOUSE_PRESSED, 0, e.getModifiers(), getWidth() / 2, getHeight() / 2, 1, false), + myContext, myPlace, myPresentation, ActionManager.getInstance(), e.getModifiers()); + if (ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) { + ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); + actionManager.fireBeforeActionPerformed(myAction, myContext, event); + Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(event.getDataContext()); + if (component != null && !isInTree(component)) { + return; + } + myAction.actionPerformed(event); + actionManager.queueActionPerformedEvent(myAction, myContext, event); + } } - myAction.actionPerformed(event); - actionManager.queueActionPerformedEvent(myAction, myContext, event); - } + }); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 3c09eaa8d853..4c679ded4657 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -69,7 +69,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark private static final Icon ERRORS_FOUND_ICON = IconLoader.getIcon("/general/errorsFound.png"); private static final int ERROR_ICON_WIDTH = ERRORS_FOUND_ICON.getIconWidth(); private static final int ERROR_ICON_HEIGHT = ERRORS_FOUND_ICON.getIconHeight(); - private static final int PREFERRED_WIDTH = ERRORS_FOUND_ICON.getIconWidth() + 4; + private static final int PREFERRED_WIDTH = ERRORS_FOUND_ICON.getIconWidth() + 3; private final EditorImpl myEditor; private ErrorStripeRenderer myErrorStripeRenderer = null; private final List myErrorMarkerListeners = new ArrayList(); @@ -299,18 +299,15 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark final Rectangle bounds = getBounds(); - g.setColor(myEditor.getBackgroundColor()); + g.setColor(ButtonlessScrollBarUI.TRACK_BACKGROUND); g.fillRect(0, 0, bounds.width, bounds.height); - g.setColor(ButtonlessScrollBarUI.TRACK_BACKGROUND); - g.fillRect(3, 0, bounds.width, bounds.height); - g.setColor(ButtonlessScrollBarUI.TRACK_BORDER); - g.drawLine(3, 0, 3, bounds.height); + g.drawLine(0, 0, 0, bounds.height); try { if (myErrorStripeRenderer != null) { - myErrorStripeRenderer.paint(this, g, new Rectangle(5, 2, ERROR_ICON_WIDTH, ERROR_ICON_HEIGHT)); + myErrorStripeRenderer.paint(this, g, new Rectangle(3, 2, ERROR_ICON_WIDTH, ERROR_ICON_HEIGHT)); } } finally { @@ -354,15 +351,9 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - g.translate(-2, 0); - g.setColor(Color.white); - final Rectangle oldClip = g.getClipBounds(); - g.setClip(thumbBounds.x, thumbBounds.y, 5, thumbBounds.height ); - g.fillRect(thumbBounds.x + 2, thumbBounds.y + 2, thumbBounds.width, thumbBounds.height - 4); - - g.setClip(oldClip); + g.translate(5, 0); super.paintThumb(g, c, thumbBounds); - g.translate(2, 0); + g.translate(-5, 0); } @Override @@ -372,38 +363,27 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected int getThickness() { - return super.getThickness() + 5; + return super.getThickness() + 4; } @Override protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { - Rectangle bounds = new Rectangle(trackBounds); - - g.setColor(myEditor.getBackgroundColor()); - g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); - - bounds.width /= 1.20; - final int shift = trackBounds.width - bounds.width; - - g.translate(shift, 0); - - super.paintTrack(g, c, bounds); + super.paintTrack(g, c, trackBounds); ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); try { Rectangle clipBounds = g.getClipBounds(); - repaint(g, ERROR_ICON_WIDTH - 2, clipBounds); + repaint(g, ERROR_ICON_WIDTH - 1, clipBounds); } finally { - g.translate(-shift, 0); ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintFinish(); } } @Override protected Color adjustColor(Color c) { - return ColorUtil.withAlpha(super.adjustColor(c), 0.85); + return ColorUtil.withAlpha(ColorUtil.shift(super.adjustColor(c), 0.9), 0.85); } private void repaint(final Graphics g, final int width, Rectangle clipBounds) { @@ -505,7 +485,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark int i = stripes.indexOf(endingStripe); stripes.remove(i); if (i == 0) { - // visible + // visible f drawSpot(g, width, endingStripe.thin, yStart, endingStripe.yEnd, endingStripe.color, true, true); yStart = endingStripe.yEnd; } @@ -521,12 +501,12 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark Color color, boolean drawTopDecoration, boolean drawBottomDecoration) { - int x = 3; + int x = 4; int paintWidth = width; if (thinErrorStripeMark) { paintWidth /= 2; paintWidth += 1; - x += paintWidth + 1; + x = 0; } if (color == null) return; g.setColor(color); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java index 1d741114ac01..6b2644bf6f91 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java @@ -26,6 +26,7 @@ import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.EditorsSplitters; import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; @@ -34,7 +35,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Set; public abstract class FileEditorManagerEx extends FileEditorManager { protected final List myDataProviders = new ArrayList(); @@ -71,6 +74,8 @@ public abstract class FileEditorManagerEx extends FileEditorManager { */ public abstract EditorWindow getCurrentWindow(); + public abstract AsyncResult getActiveWindow(); + public abstract void setCurrentWindow(EditorWindow window); /** @@ -152,9 +157,12 @@ public abstract class FileEditorManagerEx extends FileEditorManager { public void refreshIcons() { if (this instanceof FileEditorManagerImpl) { final FileEditorManagerImpl mgr = (FileEditorManagerImpl)this; - final EditorsSplitters splitters = mgr.getSplitters(); - for (VirtualFile file : mgr.getOpenFiles()) { - splitters.updateFileIcon(file); + Set splitters = mgr.getAllSplitters(); + for (Iterator iterator = splitters.iterator(); iterator.hasNext();) { + EditorsSplitters each = iterator.next(); + for (VirtualFile file : mgr.getOpenFiles()) { + each.updateFileIcon(file); + } } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java new file mode 100644 index 000000000000..2e98f3cf6d65 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.fileEditor.impl; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockContainerFactory; +import com.intellij.ui.docking.DockManager; +import org.jdom.Element; + +public class DockableEditorContainerFactory implements DockContainerFactory.Persistent { + + public static final String TYPE = "file-editors"; + + private Project myProject; + private FileEditorManagerImpl myFileEditorManager; + private DockManager myDockManager; + + public DockableEditorContainerFactory(Project project, FileEditorManagerImpl fileEditorManager, DockManager dockManager) { + this.myProject = project; + myFileEditorManager = fileEditorManager; + myDockManager = dockManager; + } + + @Override + public DockContainer createContainer() { + final Ref container = new Ref(); + EditorsSplitters splitters = new EditorsSplitters(myFileEditorManager, myDockManager, false) { + @Override + protected void afterFileClosed(VirtualFile file) { + container.get().fireContentClosed(file); + } + + @Override + protected void afterFileOpen(VirtualFile file) { + container.get().fireContentOpen(file); + } + }; + splitters.createCurrentWindow(); + container.set(new DockableEditorTabbedContainer(myProject, myDockManager, splitters, true)); + return container.get(); + } + + @Override + public DockContainer loadContainerFrom(Element element) { + DockableEditorTabbedContainer container = (DockableEditorTabbedContainer)createContainer(); + container.getSplitters().readExternal(element.getChild("state")); + return container; + } + + @Override + public void dispose() { + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java new file mode 100644 index 000000000000..353c8d9ce87a --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -0,0 +1,234 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.fileEditor.impl; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.awt.RelativeRectangle; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockContainerFactory; +import com.intellij.ui.docking.DockManager; +import com.intellij.ui.docking.DockableContent; +import com.intellij.ui.tabs.JBTabs; +import com.intellij.ui.tabs.TabInfo; +import com.intellij.ui.tabs.impl.JBTabsImpl; +import org.jdom.Element; + +import javax.swing.*; +import java.awt.*; +import java.util.concurrent.CopyOnWriteArraySet; + +class DockableEditorTabbedContainer implements DockContainer.Persistent { + + private EditorsSplitters mySplitters; + private Project myProject; + + private CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); + + private JBTabs myCurrentOver; + private Image myCurrentOverImg; + private TabInfo myCurrentOverInfo; + + private boolean myDisposeWhenEmpty; + private DockManager myDockManager; + + private boolean myWasEverShown; + + DockableEditorTabbedContainer(Project project, DockManager dockManager) { + this(project, dockManager, null, true); + } + + DockableEditorTabbedContainer(Project project, DockManager dockManager, EditorsSplitters splitters, boolean disposeWhenEmpty) { + myProject = project; + myDockManager = dockManager; + mySplitters = splitters; + myDisposeWhenEmpty = disposeWhenEmpty; + } + + @Override + public String getDockContainerType() { + return DockableEditorContainerFactory.TYPE; + } + + @Override + public Element getState() { + Element editors = new Element("state"); + mySplitters.writeExternal(editors); + return editors; + } + + void fireContentClosed(VirtualFile file) { + for (Listener each : myListeners) { + each.contentRemoved(file); + } + } + + void fireContentOpen(VirtualFile file) { + for (Listener each : myListeners) { + each.contentAdded(file); + } + } + + @Override + public RelativeRectangle getAcceptArea() { + return new RelativeRectangle(mySplitters); + } + + @Override + public boolean canAccept(DockableContent content, RelativePoint point) { + if (content instanceof EditorTabbedContainer.MyDragOutDelegate.DockableEditor) { + EditorTabbedContainer.MyDragOutDelegate.DockableEditor editor = (EditorTabbedContainer.MyDragOutDelegate.DockableEditor)content; + + JBTabs targetTabs = mySplitters.getTabsAt(point); + if (targetTabs != null) { + EditorWindow targetWindow = EditorWindow.DATA_KEY.getData(targetTabs.getDataProvider()); + if (targetWindow != editor.getEditorWindow()) return true; + } + } + + return false; + } + + @Override + public void add(DockableContent content, RelativePoint dropTarget) { + EditorWindow window = null; + if (myCurrentOver != null) { + window = EditorWindow.DATA_KEY.getData(myCurrentOver.getDataProvider()); + } + + VirtualFile file = ((EditorTabbedContainer.MyDragOutDelegate.DockableEditor)content).getFile(); + + + if (window == null) { + window = mySplitters.getOrCreateCurrentWindow(file); + } + + + if (myCurrentOver != null) { + int index = ((JBTabsImpl)myCurrentOver).getDropInfoIndex(); + file.putUserData(EditorWindow.INITIAL_INDEX_KEY, index); + } + + ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(window, file, true); + } + + @Override + public Image startDropOver(DockableContent content, RelativePoint point) { + return null; + } + + @Override + public Image processDropOver(DockableContent content, RelativePoint point) { + JBTabs current = mySplitters.getTabsAt(point); + if (myCurrentOver != null && myCurrentOver != current) { + resetDropOver(content); + } + + if (myCurrentOver == null && current != null) { + myCurrentOver = current; + Presentation presentation = content.getPresentation(); + myCurrentOverInfo = new TabInfo(new JLabel("")).setText(presentation.getText()).setIcon(presentation.getIcon()); + myCurrentOverImg = myCurrentOver.startDropOver(myCurrentOverInfo, point); + } + + if (myCurrentOver != null) { + myCurrentOver.processDropOver(myCurrentOverInfo, point); + } + + return myCurrentOverImg; + } + + @Override + public void resetDropOver(DockableContent content) { + if (myCurrentOver != null) { + myCurrentOver.resetDropOver(myCurrentOverInfo); + myCurrentOver = null; + myCurrentOverInfo = null; + myCurrentOverImg = null; + } + } + + @Override + public JComponent getComponent() { + return mySplitters; + } + + public EditorsSplitters getSplitters() { + return mySplitters; + } + + public void close(VirtualFile file) { + mySplitters.getCurrentWindow().closeFile(file); + } + + @Override + public void closeAll() { + VirtualFile[] files = mySplitters.getOpenFiles(); + for (VirtualFile each : files) { + close(each); + } + } + + @Override + public void addListener(final Listener listener, Disposable parent) { + myListeners.add(listener); + Disposer.register(parent, new Disposable() { + @Override + public void dispose() { + myListeners.remove(listener); + } + }); + } + + @Override + public boolean isEmpty() { + return mySplitters.isEmptyVisible(); + } + + @Override + public void dispose() { + closeAll(); + } + + @Override + public boolean isDisposeWhenEmpty() { + return myDisposeWhenEmpty; + } + + @Override + public void showNotify() { + if (!myWasEverShown) { + myWasEverShown = true; + IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() { + @Override + public void run() { + getSplitters().openFiles(); + } + }); + } + } + + @Override + public void hideNotify() { + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index d6dc383d19f9..dd81146ddddf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -38,6 +38,7 @@ import com.intellij.openapi.wm.*; import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.docking.*; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; import com.intellij.ui.tabs.*; @@ -68,9 +69,13 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget @NonNls public static final String HELP_ID = "ideaInterface.editor"; - EditorTabbedContainer(final EditorWindow window, Project project, int tabPlacement) { + private TabInfo.DragOutDelegate myDragOutDelegate = new MyDragOutDelegate(); + private DockManager myDockManager; + + EditorTabbedContainer(final EditorWindow window, Project project, DockManager dockManager, int tabPlacement) { myWindow = window; myProject = project; + myDockManager = dockManager; final ActionManager actionManager = ActionManager.getInstance(); myTabs = new JBTabsImpl(project, actionManager, IdeFocusManager.getInstance(project), this); myTabs.setDataProvider(new MyDataProvider()).setPopupGroup(new Getter() { @@ -267,7 +272,8 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget if (tab != null) return; tab = new TabInfo(comp).setText(calcTabTitle(myProject, file)).setIcon(icon).setTooltipText(tooltip).setObject(file) - .setTabColor(calcTabColor(myProject, file)); + .setTabColor(calcTabColor(myProject, file)) + .setDragOutDelegate(myDragOutDelegate); tab.setTestableUi(new MyQueryable(tab)); final DefaultActionGroup tabActions = new DefaultActionGroup(); @@ -277,6 +283,10 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget myTabs.addTab(tab, indexToInsert); } + public boolean isEmptyVisible() { + return myTabs.isEmptyVisible(); + } + private class MyQueryable implements Queryable { private final TabInfo myTab; @@ -382,6 +392,10 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } } + if (EditorWindow.DATA_KEY.is(dataId)) { + return myWindow; + } + return null; } } @@ -390,14 +404,20 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget TabInfo selected = myTabs.getSelectedInfo(); if (selected == null) return; - VirtualFile file = (VirtualFile)selected.getObject(); + final VirtualFile file = (VirtualFile)selected.getObject(); final FileEditorManagerEx mgr = FileEditorManagerEx.getInstanceEx(myProject); - EditorWindow wnd = mgr.getCurrentWindow(); - if (wnd != null) { - if (wnd.findFileComposite(file) != null) { - mgr.closeFile(file, wnd); + + AsyncResult window = mgr.getActiveWindow(); + window.doWhenDone(new AsyncResult.Handler() { + @Override + public void run(EditorWindow wnd) { + if (wnd != null) { + if (wnd.findFileComposite(file) != null) { + mgr.closeFile(file, wnd); + } + } } - } + }); } private class TabMouseListener extends MouseAdapter { @@ -474,4 +494,95 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget return false; } } + + class MyDragOutDelegate implements TabInfo.DragOutDelegate { + + private VirtualFile myFile; + private DragSession mySession; + + @Override + public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { + final Image img = myTabs.getComponentImage(info); + info.setHidden(true); + + myFile = (VirtualFile)info.getObject(); + Presentation presentation = new Presentation(info.getText()); + presentation.setIcon(info.getIcon()); + mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile, presentation, myWindow)); + } + + private DockManager getDockManager() { + return DockManager.getInstance(myProject); + } + + @Override + public void processDragOut(MouseEvent event, TabInfo source) { + mySession.process(event); + } + + @Override + public void dragOutFinished(MouseEvent event, TabInfo source) { + FileEditorManagerEx.getInstanceEx(myProject).closeFile(myFile, myWindow); + + mySession.process(event); + + myFile = null; + mySession = null; + } + + class DockableEditor implements DockableContent { + final Image myImg; + private DockableEditorTabbedContainer myContainer; + private Presentation myPresentation; + private EditorWindow myEditorWindow; + + + public DockableEditor(Image img, VirtualFile file, Presentation presentation, EditorWindow window) { + myImg = img; + myFile = file; + myPresentation = presentation; + myContainer = new DockableEditorTabbedContainer(myProject, myDockManager); + myEditorWindow = window; + } + + @Override + public VirtualFile getKey() { + return myFile; + } + + @Override + public Image getPreviewImage() { + return myImg; + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(myImg.getWidth(null), myImg.getHeight(null)); + } + + @Override + public String getDockContainerType() { + return DockableEditorContainerFactory.TYPE; + } + + public EditorWindow getEditorWindow() { + return myEditorWindow; + } + + @Override + public Presentation getPresentation() { + return myPresentation; + } + + @Override + public void close() { + myContainer.close(myFile); + } + + public VirtualFile getFile() { + return myFile; + } + } + } + } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 8d6bdec5de79..e3322494a183 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -87,7 +87,7 @@ public class EditorWindow { private void createTabs(int tabPlacement) { LOG.assertTrue (myTabbedPane == null); - myTabbedPane = new EditorTabbedContainer(this, getManager().getProject(), tabPlacement); + myTabbedPane = new EditorTabbedContainer(this, getManager().getProject(), getManager().getDockManager(), tabPlacement); myPanel.add(myTabbedPane.getComponent(), BorderLayout.CENTER); } @@ -131,7 +131,7 @@ public class EditorWindow { editorManager.runChange(new Runnable() { public void run() { try { - final List editors = editorManager.getEditorComposites(file); + final List editors = myOwner.findEditorComposites(file); if (editors.isEmpty()) return; final EditorWithProviderComposite editor = findFileComposite(file); @@ -170,6 +170,8 @@ public class EditorWindow { editorManager.getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER); afterPublisher.fileClosed(editorManager, file); + + myOwner.afterFileClosed(file); } } }); @@ -301,6 +303,14 @@ public class EditorWindow { } } + public EditorsSplitters getOwner() { + return myOwner; + } + + public boolean isEmptyVisible() { + return myTabbedPane != null ? myTabbedPane.isEmptyVisible() : getFiles().length == 0; + } + protected static class TComp extends JPanel implements DataProvider{ final EditorWithProviderComposite myEditor; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index c3b85661c251..d470ae45245e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -22,19 +22,25 @@ import com.intellij.openapi.fileEditor.FileEditorProvider; import com.intellij.openapi.fileEditor.FileEditorState; import com.intellij.openapi.fileEditor.FileEditorStateLevel; import com.intellij.openapi.fileEditor.impl.text.FileDropHandler; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.FocusWatcher; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.docking.DockManager; import com.intellij.ui.tabs.JBTabs; +import com.intellij.ui.tabs.impl.JBTabsImpl; import com.intellij.util.Alarm; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ArrayListSet; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,7 +56,7 @@ import java.util.List; /** * Author: msk */ -public final class EditorsSplitters extends JPanel { +public class EditorsSplitters extends JPanel { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.EditorsSplitters"); private EditorWindow myCurrentWindow; private VirtualFile myCurrentFile; @@ -60,16 +66,24 @@ public final class EditorsSplitters extends JPanel { private final MyFocusWatcher myFocusWatcher; private EditorWithProviderComposite myCurrentSelectedEditor; private final Alarm myIconUpdaterAlarm = new Alarm(); + private DockManager myDockManager; - public EditorsSplitters(final FileEditorManagerImpl manager) { + public EditorsSplitters(final FileEditorManagerImpl manager, DockManager dockManager, boolean createOwnDockableContainer) { super(new BorderLayout()); setOpaque(true); setBackground(Color.GRAY); myManager = manager; + myDockManager = dockManager; myFocusWatcher = new MyFocusWatcher(); setFocusTraversalPolicy(new MyFocusTraversalPolicy()); setTransferHandler(new MyTransferHandler()); clear(); + + if (createOwnDockableContainer) { + DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), dockManager, this, false); + Disposer.register(manager.getProject(), dockable); + myDockManager.register(dockable); + } } public FileEditorManagerImpl getManager() { @@ -430,6 +444,35 @@ public final class EditorsSplitters extends JPanel { return 0; } + protected void afterFileClosed(VirtualFile file) { + } + + protected void afterFileOpen(VirtualFile file) { + } + + public JBTabs getTabsAt(RelativePoint point) { + Point thisPoint = point.getPoint(this); + Component c = SwingUtilities.getDeepestComponentAt(this, thisPoint.x, thisPoint.y); + while (c != null) { + if (c instanceof JBTabs) { + return (JBTabs)c; + } + c = c.getParent(); + } + + return null; + } + + public boolean isEmptyVisible() { + EditorWindow[] windows = getWindows(); + for (EditorWindow each : windows) { + if (!each.isEmptyVisible()) { + return false; + } + } + return true; + } + private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy { public final Component getDefaultComponentImpl(final Container focusCycleRoot) { if (myCurrentWindow != null) { @@ -473,7 +516,7 @@ public final class EditorsSplitters extends JPanel { return getCurrentWindow(); } - private void createCurrentWindow() { + public void createCurrentWindow() { LOG.assertTrue(myCurrentWindow == null); setCurrentWindow(new EditorWindow(this)); add(myCurrentWindow.myPanel, BorderLayout.CENTER); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 9678febdb2e2..89a0eaf9267f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -53,12 +53,15 @@ import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; import com.intellij.openapi.vfs.*; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.openapi.wm.impl.FrameTitleBuilder; import com.intellij.openapi.wm.impl.IdeFrameImpl; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockManager; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.impl.MessageListenerList; @@ -74,8 +77,7 @@ import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; -import java.util.ArrayList; -import java.util.Collection; +import java.util.*; import java.util.List; /** @@ -101,13 +103,24 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec * Removes invalid myEditor and updates "modified" status. */ private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener(); + private DockManager myDockManager; + private DockableEditorContainerFactory myContentFactory; - public FileEditorManagerImpl(final Project project) { + public FileEditorManagerImpl(final Project project, DockManager dockManager) { /* ApplicationManager.getApplication().assertIsDispatchThread(); */ myProject = project; + myDockManager = dockManager; myListenerList = new MessageListenerList(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER); } + private void initDockableContentFactory() { + if (myContentFactory != null) return; + + myContentFactory = new DockableEditorContainerFactory(myProject, this, myDockManager); + myDockManager.register(DockableEditorContainerFactory.TYPE, myContentFactory); + Disposer.register(myProject, myContentFactory); + } + public static boolean isDumbAware(FileEditor editor) { return Boolean.TRUE.equals(editor.getUserData(DUMB_AWARE)); } @@ -119,11 +132,50 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return myPanels; } - public EditorsSplitters getSplitters() { + public EditorsSplitters getMainSplitters() { initUI(); + return mySplitters; } + public Set getAllSplitters() { + HashSet all = new HashSet(); + all.add(getMainSplitters()); + Set dockContainers = DockManager.getInstance(myProject).getContainers(); + for (Iterator iterator = dockContainers.iterator(); iterator.hasNext();) { + DockContainer each = iterator.next(); + if (each instanceof DockableEditorTabbedContainer) { + all.add(((DockableEditorTabbedContainer)each).getSplitters()); + } + } + + return Collections.unmodifiableSet(all); + } + + public AsyncResult getActiveSplitters() { + final AsyncResult result = new AsyncResult(); + final IdeFocusManager fm = IdeFocusManager.getInstance(myProject); + fm.doWhenFocusSettlesDown(new Runnable() { + @Override + public void run() { + Component focusOwner = fm.getFocusOwner(); + + if (focusOwner != null) { + Set splitters = getAllSplitters(); + for (EditorsSplitters each : splitters) { + if (each == focusOwner || SwingUtilities.isDescendingFrom(focusOwner, each)) { + result.setDone(each); + return; + } + } + } + + result.setDone(getMainSplitters()); + } + }); + return result; + } + private final Object myInitLock = new Object(); private void initUI() { if (myPanels == null) { @@ -131,13 +183,17 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec if (myPanels == null) { myPanels = new JPanel(new BorderLayout()); myPanels.setBorder(new MyBorder()); - mySplitters = new EditorsSplitters(this); + mySplitters = new EditorsSplitters(this, myDockManager, true); myPanels.add(mySplitters, BorderLayout.CENTER); } } } } + public DockManager getDockManager() { + return myDockManager; + } + private class MyBorder implements Border { @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { @@ -157,7 +213,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public JComponent getPreferredFocusedComponent() { assertReadAccess(); - final EditorWindow window = getSplitters().getCurrentWindow(); + final EditorWindow window = getMainSplitters().getCurrentWindow(); if (window != null) { final EditorWithProviderComposite editor = window.getSelectedEditor(); if (editor != null) { @@ -202,11 +258,11 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec * should be opened in the myEditor, otherwise the method throws an assertion. */ private void updateFileColor(final VirtualFile file) { - getSplitters().updateFileColor(file); + getMainSplitters().updateFileColor(file); } private void updateFileBackgroundColor(final VirtualFile file) { - getSplitters().updateFileBackgroundColor(file); + getMainSplitters().updateFileBackgroundColor(file); } /** @@ -214,7 +270,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec * should be opened in the myEditor, otherwise the method throws an assertion. */ protected void updateFileIcon(final VirtualFile file) { - getSplitters().updateFileIcon(file); + getMainSplitters().updateFileIcon(file); } /** @@ -232,7 +288,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec final WindowManagerEx windowManagerEx = WindowManagerEx.getInstanceEx(); final IdeFrameImpl frame = windowManagerEx.getFrame(myProject); LOG.assertTrue(frame != null); - getSplitters().updateFileName(file); + getMainSplitters().updateFileName(file); File ioFile = file == null ? null : new File(file.getPresentableUrl()); frame.setFileTitle(file == null ? null : FrameTitleBuilder.getInstance().getFileTitle(myProject, file), ioFile); } @@ -251,14 +307,14 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } public void unsplitWindow() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + final EditorWindow currentWindow = getMainSplitters().getCurrentWindow(); if (currentWindow != null) { currentWindow.unsplit(true); } } public void unsplitAllWindow() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + final EditorWindow currentWindow = getMainSplitters().getCurrentWindow(); if (currentWindow != null) { currentWindow.unsplitAll(); } @@ -266,16 +322,16 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @Override public int getWindowSplitCount() { - return getSplitters().getSplitCount(); + return getMainSplitters().getSplitCount(); } @NotNull public EditorWindow[] getWindows() { - return getSplitters().getWindows(); + return getMainSplitters().getWindows(); } public EditorWindow getNextWindow(@NotNull final EditorWindow window) { - final EditorWindow[] windows = getSplitters().getOrderedWindows(); + final EditorWindow[] windows = getMainSplitters().getOrderedWindows(); for (int i = 0; i != windows.length; ++i) { if (windows[i].equals(window)) { return windows[(i + 1) % windows.length]; @@ -286,7 +342,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } public EditorWindow getPrevWindow(@NotNull final EditorWindow window) { - final EditorWindow[] windows = getSplitters().getOrderedWindows(); + final EditorWindow[] windows = getMainSplitters().getOrderedWindows(); for (int i = 0; i != windows.length; ++i) { if (windows[i].equals(window)) { return windows[(i + windows.length - 1) % windows.length]; @@ -303,7 +359,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } // otherwise we'll split the current window, if any else { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + final EditorWindow currentWindow = getMainSplitters().getCurrentWindow(); if (currentWindow != null) { currentWindow.split(orientation, true, null, false); } @@ -311,7 +367,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } public void changeSplitterOrientation() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + final EditorWindow currentWindow = getMainSplitters().getCurrentWindow(); if (currentWindow != null) { currentWindow.changeOrientation(); } @@ -348,7 +404,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public boolean isInSplitter() { - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + final EditorWindow currentWindow = getMainSplitters().getCurrentWindow(); return currentWindow != null && currentWindow.inSplitter(); } @@ -358,15 +414,27 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } public VirtualFile getCurrentFile() { - return getSplitters().getCurrentFile(); + return getMainSplitters().getCurrentFile(); + } + + public AsyncResult getActiveWindow() { + final AsyncResult result = new AsyncResult(); + getActiveSplitters().doWhenDone(new AsyncResult.Handler() { + @Override + public void run(EditorsSplitters editorsSplitters) { + result.setDone(editorsSplitters.getCurrentWindow()); + } + }); + + return result; } public EditorWindow getCurrentWindow() { - return getSplitters().getCurrentWindow(); + return getActiveWindow().getResult(); } public void setCurrentWindow(final EditorWindow window) { - getSplitters().setCurrentWindow(window, true); + getMainSplitters().setCurrentWindow(window, true); } public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) { @@ -376,7 +444,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public void run() { if (window.isFileOpen(file)) { window.closeFile(file); - final List windows = getSplitters().findWindows(file); + final List windows = window.getOwner().findWindows(file); if (windows.isEmpty()) { // no more windows containing this file left final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); if (request != null) { @@ -425,9 +493,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private void closeFileImpl(@NotNull final VirtualFile file, final boolean moveFocus) { assertDispatchThread(); - getSplitters().runChange(new Runnable() { + getMainSplitters().runChange(new Runnable() { public void run() { - final List windows = getSplitters().findWindows(file); + final List windows = getMainSplitters().findWindows(file); if (!windows.isEmpty()) { final VirtualFile nextFile = findNextFile(file); for (final EditorWindow window : windows) { @@ -460,10 +528,10 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec throw new IllegalArgumentException("file is not valid: " + file); } assertDispatchThread(); - return openFileImpl2(getSplitters().getOrCreateCurrentWindow(file), file, focusEditor); + return openFileImpl2(getMainSplitters().getOrCreateCurrentWindow(file), file, focusEditor); } - @NotNull Pair openFileImpl2(@NotNull final EditorWindow window, + @NotNull public Pair openFileImpl2(@NotNull final EditorWindow window, @NotNull final VirtualFile file, final boolean focusEditor) { final Ref> result = new Ref>(); @@ -600,7 +668,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } // Notify editors about selection changes - getSplitters().setCurrentWindow(window, false); + window.getOwner().setCurrentWindow(window, false); + window.getOwner().afterFileOpen(file); + newSelectedComposite.getSelectedEditor().selectNotify(); if (newEditorCreated) { @@ -788,7 +858,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public Editor getSelectedTextEditor() { assertReadAccess(); - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + final EditorWindow currentWindow = getMainSplitters().getCurrentWindow(); if (currentWindow != null) { final EditorWithProviderComposite selectedEditor = currentWindow.getSelectedEditor(); if (selectedEditor != null && selectedEditor.getSelectedEditor() instanceof TextEditor) { @@ -819,6 +889,11 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return getSplitters().getSelectedEditors(); } + private EditorsSplitters getSplitters() { + EditorsSplitters active = getActiveSplitters().getResult(); + return active == null ? getMainSplitters() : active; + } + public FileEditor getSelectedEditor(@NotNull final VirtualFile file) { final Pair selectedEditorWithProvider = getSelectedEditorWithProvider(file); return selectedEditorWithProvider == null ? null : selectedEditorWithProvider.getFirst(); @@ -895,7 +970,12 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @NotNull public List getEditorComposites(final VirtualFile file) { - return getSplitters().findEditorComposites(file); + ArrayList result = new ArrayList(); + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + result.addAll(each.findEditorComposites(file)); + } + return result; } @NotNull @@ -964,7 +1044,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public void projectOpened() { //myFocusWatcher.install(myWindows.getComponent ()); - getSplitters().startListeningFocus(); + getMainSplitters().startListeningFocus(); MessageBusConnection connection = myProject.getMessageBus().connect(myProject); @@ -997,7 +1077,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { public void run() { setTabsMode(UISettings.getInstance().EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); - getSplitters().openFiles(); + getMainSplitters().openFiles(); + initDockableContentFactory(); + LaterInvocator.invokeLater(new Runnable() { public void run() { long currentTime = System.nanoTime(); @@ -1019,7 +1101,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public void projectClosed() { //myFocusWatcher.deinstall(myWindows.getComponent ()); - getSplitters().dispose(); + getMainSplitters().dispose(); // Dispose created editors. We do not use use closeEditor method because // it fires event and changes history. @@ -1033,18 +1115,20 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return "FileEditorManager"; } - public void initComponent() { /* really do nothing */ } + public void initComponent() { + + } public void disposeComponent() { /* really do nothing */ } //JDOMExternalizable methods public void writeExternal(final Element element) { - getSplitters().writeExternal(element); + getMainSplitters().writeExternal(element); } public void readExternal(final Element element) { - getSplitters().readExternal(element); + getMainSplitters().readExternal(element); } private EditorWithProviderComposite getEditorComposite(@NotNull final FileEditor editor) { @@ -1357,15 +1441,15 @@ private final class MyVirtualFileListener extends VirtualFileAdapter { public void uiSettingsChanged(final UISettings source) { assertDispatchThread(); setTabsMode(source.EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); - getSplitters().setTabsPlacement(source.EDITOR_TAB_PLACEMENT); - getSplitters().trimToSize(source.EDITOR_TAB_LIMIT); + getMainSplitters().setTabsPlacement(source.EDITOR_TAB_PLACEMENT); + getMainSplitters().trimToSize(source.EDITOR_TAB_LIMIT); // Tab layout policy if (source.SCROLL_TAB_LAYOUT_IN_EDITOR) { - getSplitters().setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + getMainSplitters().setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); } else { - getSplitters().setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + getMainSplitters().setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); } // "Mark modified files with asterisk" diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java index 6acd547148b3..deb6fd93a510 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java @@ -29,7 +29,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.FocusWatcher; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ex.WindowManagerEx; -import com.intellij.openapi.wm.impl.IdeFrameImpl; +import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; import com.intellij.ui.FocusTrackback; import com.intellij.util.ImageLoader; import com.intellij.util.containers.HashMap; @@ -56,7 +56,7 @@ public class FrameWrapper implements Disposable { private FocusWatcher myFocusWatcher; private ActionCallback myFocusedCallback; - private boolean isDisposed; + private boolean myDisposed; public FrameWrapper() { } @@ -119,16 +119,20 @@ public class FrameWrapper implements Disposable { } public void close() { - getFrame().setVisible(false); - getFrame().dispose(); + Disposer.dispose(this); } public void dispose() { - isDisposed = true; + if (isDisposed()) return; + + getFrame().setVisible(false); + getFrame().dispose(); + + myDisposed = true; } public boolean isDisposed() { - return isDisposed; + return myDisposed; } private void addCloseOnEsc(final JFrame frame) { @@ -151,14 +155,18 @@ public class FrameWrapper implements Disposable { } public JFrame getFrame() { - assert !isDisposed : "Already disposed!"; + assert !myDisposed : "Already disposed!"; if (myFrame == null) { - myFrame = new MyJFrame(); + myFrame = createJFrame(); } return myFrame; } + protected JFrame createJFrame() { + return new MyJFrame(); + } + public void setComponent(JComponent component) { myComponent = component; } @@ -226,6 +234,7 @@ public class FrameWrapper implements Disposable { private boolean myDisposing; private MyJFrame() throws HeadlessException { + setGlassPane(new IdeGlassPaneImpl(getRootPane())); } public void dispose() { @@ -261,6 +270,14 @@ public class FrameWrapper implements Disposable { } } + public void setLocation(Point location) { + getFrame().setLocation(location); + } + + public void setSize(Dimension size) { + getFrame().setSize(size); + } + private class MyProjectManagerListener extends ProjectManagerAdapter { public void projectClosing(Project project) { if (project == myProject) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index 4f814a16f2b7..864f6dc754b0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -17,6 +17,8 @@ package com.intellij.openapi.wm.impl; import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationAdapter; import com.intellij.openapi.application.ApplicationManager; @@ -83,6 +85,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { private final Map> myLastFocused = new HashMap>(); private final Map> myLastFocusedAtDeactivation = new HashMap>(); + private DataContext myRunContext; + public FocusManagerImpl(WindowManager wm) { myApp = ApplicationManager.getApplication(); myQueue = IdeEventQueue.getInstance(); @@ -337,6 +341,11 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } public void doWhenFocusSettlesDown(@NotNull final Runnable runnable) { + if (myRunContext != null) { + runnable.run(); + return; + } + final boolean needsRestart = isIdleQueueEmpty(); myIdleRequests.add(runnable); @@ -414,6 +423,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } public boolean isFocusTransferReady() { + if (myRunContext != null) return true; + invalidateFocusRequestsQueue(); if (!myFocusRequests.isEmpty()) return false; @@ -496,6 +507,30 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { return new FurtherRequestor(this, getTimestamp(true)); } + @Override + public Component getFocusOwner() { + Component result = null; + if (myRunContext != null) { + result = (Component)myRunContext.getData(PlatformDataKeys.CONTEXT_COMPONENT.getName()); + } + + if (result == null) { + result = isFocusBeingTransferred() ? null : KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + } + + return result; + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + myRunContext = context; + try { + runnable.run(); + } finally { + myRunContext = null; + } + } + private static class FurtherRequestor implements FocusRequestor { private final IdeFocusManager myManager; private final Expirable myExpirable; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java index e0f0a30ccaa5..ded95f259b16 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm.impl; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; import com.intellij.openapi.wm.FocusCommand; @@ -87,4 +88,14 @@ public class IdeFocusManagerHeadless extends IdeFocusManager { public FocusRequestor getFurtherRequestor() { return this; } + + @Override + public Component getFocusOwner() { + return null; + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + runnable.run(); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java index ebb8754eb51e..1d97067c18a7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm.impl; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; import com.intellij.openapi.wm.FocusCommand; @@ -86,6 +87,16 @@ public class IdeFocusManagerImpl extends IdeFocusManager { return getGlobalInstance().getFurtherRequestor(); } + @Override + public Component getFocusOwner() { + return getGlobalInstance().getFocusOwner(); + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + getGlobalInstance().runOnOwnContext(context, runnable); + } + public boolean isFocusBeingTransferred() { return getGlobalInstance().isFocusBeingTransferred(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java index 7773f8596b5a..16a4f059e9a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java @@ -74,6 +74,10 @@ public final class TestWindowManager extends WindowManagerEx implements Applicat return new Dimension(0, 0); } + @Override + public void setInfo(@Nullable String s, @Nullable String requestor) { + } + public boolean isVisible() { return false; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 350be8fb8329..d68077e8046e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -273,6 +273,8 @@ public final class WindowManagerImpl extends WindowManagerEx implements Applicat ((JWindow)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } else if (window instanceof JDialog) { ((JDialog)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); + } else if (window instanceof JFrame) { + ((JFrame)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } } else if (AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.TRANSLUCENT)) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java index a48e302f3982..f12b2bf29612 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java @@ -280,10 +280,15 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx { } public void setInfo(@Nullable final String s) { + setInfo(s, null); + } + + @Override + public void setInfo(@Nullable final String s, @Nullable final String requestor) { UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { myInfo = s; - if (myInfoAndProgressPanel != null) myInfoAndProgressPanel.setText(s); + if (myInfoAndProgressPanel != null) myInfoAndProgressPanel.setText(s, requestor); } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java index cfa169289027..967852aca3f7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java @@ -72,6 +72,8 @@ public class InfoAndProgressPanel extends JPanel implements CustomStatusBarWidge private AsyncProcessIcon myRefreshIcon; private EmptyIcon myEmptyRefreshIcon; + private String myCurrentRequestor; + public InfoAndProgressPanel() { setOpaque(false); @@ -323,8 +325,15 @@ public class InfoAndProgressPanel extends JPanel implements CustomStatusBarWidge myRefreshAndInfoPanel.repaint(); } - public void setText(final String text) { + public void setText(final String text, final String requestor) { + if (text == null || text.length() == 0) { + if (requestor == null && myCurrentRequestor == null) return; + if (requestor != null && !requestor.equals(myCurrentRequestor)) return; + if (myCurrentRequestor != null && !myCurrentRequestor.equals(requestor)) return; + } + myInfoPanel.setText(text); + myCurrentRequestor = requestor; } public void setRefreshVisible(final boolean visible) { diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java new file mode 100644 index 000000000000..0f1884210ea8 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -0,0 +1,453 @@ +/* + * Copyright 2000-2010 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.ui.docking.impl; + +import com.intellij.ide.IdeEventQueue; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.FrameWrapper; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.BusyObject; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.ex.WindowManagerEx; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.awt.RelativeRectangle; +import com.intellij.ui.docking.*; +import com.intellij.util.ui.update.UiNotifyConnector; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.LineBorder; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.util.*; +import java.util.List; + +@State( + name = "DockManager", + storages = {@Storage( + id = "other", + file = "$WORKSPACE_FILE$")}) + +public class DockManagerImpl extends DockManager implements PersistentStateComponent{ + + private Project myProject; + + private Map myFactories = new HashMap(); + + private Set myContainers = new HashSet(); + private Map myWindows = new HashMap(); + + private MyDragSession myCurrentDragSession; + + private BusyObject.Impl myBusyObject = new BusyObject.Impl() { + @Override + protected boolean isReady() { + return myCurrentDragSession == null; + } + }; + + private int myWindowIdCounter = 1; + + private Element myLoadedState; + + public DockManagerImpl(Project project) { + myProject = project; + } + + public void register(final DockContainer container) { + myContainers.add(container); + Disposer.register(container, new Disposable() { + @Override + public void dispose() { + myContainers.remove(container); + } + }); + } + + @Override + public void register(final String id, DockContainerFactory factory) { + myFactories.put(id, factory); + Disposer.register(factory, new Disposable() { + @Override + public void dispose() { + myFactories.remove(id); + } + }); + + readStateFor(id); + } + + @Override + public Set getContainers() { + return Collections.unmodifiableSet(myContainers); + } + + @Override + public DragSession createDragSession(MouseEvent mouseEvent, DockableContent content) { + stopCurrentDragSession(); + + for (DockContainer each : myContainers) { + if (each.isEmpty() && each.isDisposeWhenEmpty()) { + DockWindow window = myWindows.get(each); + if (window != null) { + window.setTransparrent(true); + } + } + } + + myCurrentDragSession = new MyDragSession(mouseEvent, content); + return myCurrentDragSession; + } + + + private void stopCurrentDragSession() { + if (myCurrentDragSession != null) { + myCurrentDragSession.cancel(); + myCurrentDragSession = null; + myBusyObject.onReady(); + + for (DockContainer each : myContainers) { + if (!each.isEmpty()) { + DockWindow window = myWindows.get(each); + if (window != null) { + window.setTransparrent(false); + } + } + } + } + } + + private ActionCallback getReady() { + return myBusyObject.getReady(this); + } + + @Override + public void projectOpened() { + } + + @Override + public void projectClosed() { + } + + @NotNull + @Override + public String getComponentName() { + return "DockManager"; + } + + @Override + public void initComponent() { + } + + @Override + public void disposeComponent() { + } + + private class MyDragSession implements DragSession { + + private JWindow myWindow; + + private Image myDragImage; + private Image myDefaultDragImage; + + private DockableContent myContent; + + private DockContainer myCurrentOverContainer; + private JLabel myImageContainer; + + private MyDragSession(MouseEvent me, DockableContent content) { + myWindow = new JWindow(); + myContent = content; + + Image previewImage = content.getPreviewImage(); + + double requiredSize = 220; + + double width = previewImage.getWidth(null); + double height = previewImage.getHeight(null); + + double ratio; + if (width > height) { + ratio = requiredSize / width; + } + else { + ratio = requiredSize / height; + } + + BufferedImage buffer = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB); + buffer.createGraphics().drawImage(previewImage, 0, 0, (int)width, (int)height, null); + + myDefaultDragImage = buffer.getScaledInstance((int)(width * ratio), (int)(height * ratio), Image.SCALE_SMOOTH); + myDragImage = myDefaultDragImage; + + myWindow.getContentPane().setLayout(new BorderLayout()); + myImageContainer = new JLabel(new ImageIcon(myDragImage)); + myImageContainer.setBorder(new LineBorder(Color.lightGray)); + myWindow.getContentPane().add(myImageContainer, BorderLayout.CENTER); + + setLocationFrom(me); + + myWindow.setVisible(true); + + WindowManagerEx.getInstanceEx().setAlphaModeEnabled(myWindow, true); + WindowManagerEx.getInstanceEx().setAlphaModeRatio(myWindow, 0.1f); + myWindow.getRootPane().putClientProperty("Window.shadow", Boolean.FALSE); + } + + private void setLocationFrom(MouseEvent me) { + Point showPoint = me.getPoint(); + SwingUtilities.convertPointToScreen(showPoint, me.getComponent()); + + showPoint.x -= myDragImage.getWidth(null) / 2; + showPoint.y += 10; + myWindow.setBounds(new Rectangle(showPoint, new Dimension(myDragImage.getWidth(null), myDragImage.getHeight(null)))); + } + + @Override + public void process(MouseEvent e) { + RelativePoint point = new RelativePoint(e); + + Image img = null; + if (e.getID() == MouseEvent.MOUSE_DRAGGED) { + DockContainer over = findContainerFor(point, myContent); + if (myCurrentOverContainer != null && myCurrentOverContainer != over) { + myCurrentOverContainer.resetDropOver(myContent); + myCurrentOverContainer = null; + } + + if (myCurrentOverContainer == null && over != null) { + myCurrentOverContainer = over; + img = myCurrentOverContainer.startDropOver(myContent, point); + } + + if (myCurrentOverContainer != null) { + img = myCurrentOverContainer.processDropOver(myContent, point); + } + + if (img == null) { + img = myDefaultDragImage; + } + + if (img != myDragImage) { + myDragImage = img; + myImageContainer.setIcon(new ImageIcon(myDragImage)); + myWindow.pack(); + } + + setLocationFrom(e); + } + else if (e.getID() == MouseEvent.MOUSE_RELEASED) { + if (myCurrentOverContainer == null) { + createNewDockContainerFor(myContent, point); + stopCurrentDragSession(); + } else { + myCurrentOverContainer.add(myContent, point); + stopCurrentDragSession(); + } + } + } + + public void cancel() { + myWindow.dispose(); + + if (myCurrentOverContainer != null) { + myCurrentOverContainer.resetDropOver(myContent); + myCurrentOverContainer = null; + } + } + } + + @Nullable + private DockContainer findContainerFor(RelativePoint point, DockableContent content) { + for (DockContainer each : myContainers) { + RelativeRectangle rec = each.getAcceptArea(); + if (rec.contains(point) && each.canAccept(content, point)) { + return each; + } + } + + return null; + } + + + + private DockContainerFactory getFactory(String type) { + assert myFactories.containsKey(type) : "No factory for content type=" + type; + return myFactories.get(type); + } + + private void createNewDockContainerFor(DockableContent content, RelativePoint point) { + DockContainer container = getFactory(content.getDockContainerType()).createContainer(); + register(container); + + DockWindow window = createWindowFor(null, container); + + Dimension size = content.getPreferredSize(); + Point showPoint = point.getScreenPoint(); + showPoint.x -= size.width / 2; + showPoint.y -= size.height / 2; + + window.show(); + + window.setLocation(showPoint); + window.setSize(size); + + container.add(content, new RelativePoint(showPoint)); + } + + private DockWindow createWindowFor(@Nullable String id, DockContainer container) { + DockWindow window = new DockWindow(id != null ? id : String.valueOf(myWindowIdCounter++) , myProject, container); + window.setDimensionKey("dock-window-" + id); + myWindows.put(container, window); + window.show(); + return window; + } + + private class DockWindow extends FrameWrapper implements IdeEventQueue.EventDispatcher { + + private String myId; + private DockContainer myContainer; + + private DockWindow(String id, Project project, DockContainer container) { + myId = id; + myContainer = container; + setProject(project); + setComponent(myContainer.getComponent()); + addDisposable(container); + + IdeEventQueue.getInstance().addPostprocessor(this, this); + + myContainer.addListener(new DockContainer.Listener.Adapter() { + @Override + public void contentRemoved(Object key) { + getReady().doWhenDone(new Runnable() { + @Override + public void run() { + if (myContainer.isEmpty()) { + close(); + } + } + }); + } + }, this); + } + + public void setTransparrent(boolean transparrent) { + if (transparrent) { + WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true); + WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 1f); + } else { + WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true); + WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 0f); + } + } + + @Override + public void dispose() { + super.dispose(); + myWindows.remove(myContainer); + } + + @Override + public boolean dispatch(AWTEvent e) { + if (e instanceof KeyEvent) { + if (myCurrentDragSession != null) { + stopCurrentDragSession(); + } + } + return false; + } + + @Override + protected JFrame createJFrame() { + JFrame frame = super.createJFrame(); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + myContainer.closeAll(); + } + }); + + new UiNotifyConnector(frame.getContentPane(), myContainer); + + return frame; + } + } + + @Override + public Element getState() { + Element root = new Element("DockManager"); + for (DockContainer each : myContainers) { + DockWindow eachWindow = myWindows.get(each); + if (eachWindow != null) { + if (each instanceof DockContainer.Persistent) { + DockContainer.Persistent eachContainer = (DockContainer.Persistent)each; + Element eachWindowElement = new Element("window"); + eachWindowElement.setAttribute("id", eachWindow.myId); + Element content = new Element("content"); + content.setAttribute("type", eachContainer.getDockContainerType()); + content.addContent(eachContainer.getState()); + eachWindowElement.addContent(content); + + root.addContent(eachWindowElement); + } + } + } + return root; + } + + @Override + public void loadState(Element state) { + myLoadedState = state; + } + + private void readStateFor(String type) { + if (myLoadedState == null) return; + + List windows = myLoadedState.getChildren("window"); + for (int i = 0; i < windows.size(); i++) { + Element eachWindow = (Element)windows.get(i); + if (eachWindow == null) continue; + + String eachId = eachWindow.getAttributeValue("id"); + + Element eachContent = eachWindow.getChild("content"); + if (eachContent == null) continue; + + String eachType = eachContent.getAttributeValue("type"); + if (eachType == null || !type.equals(eachType) || !myFactories.containsKey(eachType)) continue; + + DockContainerFactory factory = myFactories.get(eachType); + if (!(factory instanceof DockContainerFactory.Persistent)) continue; + + DockContainerFactory.Persistent persistentFactory = (DockContainerFactory.Persistent)factory; + DockContainer container = persistentFactory.loadContainerFrom(eachContent); + register(container); + + createWindowFor(eachId, container); + } + } +} diff --git a/platform/platform-resources-en/src/messages/VcsBundle.properties b/platform/platform-resources-en/src/messages/VcsBundle.properties index 82b68f6c7574..72402ea1a7e9 100644 --- a/platform/platform-resources-en/src/messages/VcsBundle.properties +++ b/platform/platform-resources-en/src/messages/VcsBundle.properties @@ -541,3 +541,6 @@ options.background.processes.title=Background processes options.background.processes.enable=Enable background processes options.check.changed.on.server.title="Changed on server" conflicts revision.changes.not.supported=Current VCS does not support viewing changes by revision +line.annotation.aspect.author=Author +line.annotation.aspect.date=Date +line.annotation.aspect.revision=Revision diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 9b74ee03aa30..ca0057025d80 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -73,6 +73,7 @@ ide.tooltip.autoDismissDeadZone=300 ide.tabbedPane.bufferedPaint=true +ide.tabbedPane.dragOutMultiplier=1.2 ide.use.native.mac.filechooser=false ide.mac.filechooser.showhidden.files=false diff --git a/platform/platform-resources/src/componentSets/Platform.xml b/platform/platform-resources/src/componentSets/Platform.xml index d56b01e93d47..a99994ac7cfd 100644 --- a/platform/platform-resources/src/componentSets/Platform.xml +++ b/platform/platform-resources/src/componentSets/Platform.xml @@ -161,6 +161,11 @@ com.intellij.openapi.project.DumbModeIndicator + + com.intellij.ui.docking.DockManager + com.intellij.ui.docking.impl.DockManagerImpl + + com.intellij.notification.impl.ProjectNotificationsComponent diff --git a/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java b/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java index 378fa1898df7..3f5c1a88eb7e 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java @@ -32,6 +32,7 @@ import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -211,6 +212,11 @@ import java.util.Map; return null; } + @Override + public AsyncResult getActiveWindow() { + return new AsyncResult.Done(null); + } + @Override public void setCurrentWindow(EditorWindow window) { } diff --git a/platform/util/src/com/intellij/ui/awt/RelativePoint.java b/platform/util/src/com/intellij/ui/awt/RelativePoint.java index 90598966ed38..4c36294730f4 100644 --- a/platform/util/src/com/intellij/ui/awt/RelativePoint.java +++ b/platform/util/src/com/intellij/ui/awt/RelativePoint.java @@ -21,14 +21,25 @@ import java.awt.event.MouseEvent; public class RelativePoint { - private final Component myComponent; - private final Point myPointOnComponent; + private Component myComponent; + private Point myPointOnComponent; public RelativePoint(MouseEvent event) { - this(event.getComponent(), event.getPoint()); + init(event.getComponent(), event.getPoint()); } public RelativePoint(Component aComponent, Point aPointOnComponent) { + init(aComponent, aPointOnComponent); + } + + public RelativePoint(Point screenPoint) { + Point p = new Point(screenPoint.x, screenPoint.y); + Frame c = JOptionPane.getRootFrame(); + SwingUtilities.convertPointFromScreen(p, c); + init(c, p); + } + + private void init(Component aComponent, Point aPointOnComponent) { if (aComponent.isShowing()) { myComponent = SwingUtilities.getRootPane(aComponent); myPointOnComponent = SwingUtilities.convertPoint(aComponent, aPointOnComponent, myComponent); @@ -103,4 +114,10 @@ public class RelativePoint { final Point point = new Point(visibleRect.x + visibleRect.width, visibleRect.y); return new RelativePoint(component, point); } + + public static RelativePoint fromScreen(Point screenPoint) { + Frame root = JOptionPane.getRootFrame(); + SwingUtilities.convertPointFromScreen(screenPoint, root); + return new RelativePoint(root, screenPoint); + } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java index 7b14c09c9f56..33b27700f7c7 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java @@ -15,26 +15,51 @@ */ package com.intellij.openapi.vcs.annotate; +import com.intellij.openapi.vcs.VcsBundle; import org.jetbrains.annotations.Nullable; /** * Represents one part of a line annotation which is shown in the editor when the "Annotate" * action is invoked. Classes implementing this interface can also implement - * {@link com.intellij.openapi.editor.EditorGutterAction} to handle clicks on the annotation. + * {@link com.intellij.openapi.editor.EditorGutterAction} to handle clicks on the annotation. * + * @author Konstantin Bulenkov * @see FileAnnotation#getAspects() */ public interface LineAnnotationAspect { + String AUTHOR = VcsBundle.message("line.annotation.aspect.author"); + String DATE = VcsBundle.message("line.annotation.aspect.date"); + String REVISION = VcsBundle.message("line.annotation.aspect.revision"); /** - * Get annotation text for the spcific line number - * @param lineNumber the line number to query - * @return the annotation text + * Get annotation text for the specific line number + * + * @param line the line number to query + * @return the annotation text */ - String getValue(int lineNumber); + String getValue(int line); /** - * used if return value is not null + * Used to show a tooltip for specific line or group of lines + * + * @param line the line number to query + * @return the tooltip text for the line */ @Nullable - String getTooltipText(int lineNumber); + String getTooltipText(int line); + + /** + * Returns unique identifier, that will be used to show/hide some aspects + * If null this line aspect won't be configurable in annotation settings + * + * @return unique id + */ + @Nullable + String getId(); + + /** + * Returns true if this aspect will be shown on Annotate action + * + * @return true if this aspect will be shown on Annotate action + */ + boolean isShowByDefault(); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java index 7fca123359ba..700adf41a409 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java @@ -19,11 +19,40 @@ import com.intellij.openapi.editor.EditorGutterAction; import java.awt.*; +/** + * @author Konstantin Bulenkov + */ public abstract class LineAnnotationAspectAdapter implements LineAnnotationAspect, EditorGutterAction { + private final String myId; + private final boolean myShowByDefault; + + protected LineAnnotationAspectAdapter() { + this(null, false); + } + + protected LineAnnotationAspectAdapter(String id) { + this(id, false); + } + + public LineAnnotationAspectAdapter(String id, boolean showByDefault) { + myId = id; + myShowByDefault = showByDefault; + } + public String getTooltipText(int lineNumber) { return null; } + @Override + public String getId() { + return myId; + } + + @Override + public boolean isShowByDefault() { + return myShowByDefault; + } + public Cursor getCursor(final int lineNum) { return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); } diff --git a/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java b/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java index c450e35b7b8c..9eea6b38d768 100644 --- a/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java +++ b/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.vcsUtil; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; @@ -33,6 +34,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.actions.VcsContextFactory; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; @@ -579,4 +581,15 @@ public class VcsUtil { return (! s1Trimmed.equals(s2Trimmed)) && s1Trimmed.equalsIgnoreCase(s2Trimmed); } + + private static String ANNO_ASPECT = "show.vcs.annotation.aspect."; + public static boolean isAspectAvailableByDefault(LineAnnotationAspect aspect) { + if (aspect.getId() == null) return aspect.isShowByDefault(); + return PropertiesComponent.getInstance().getBoolean(ANNO_ASPECT + aspect.getId(), aspect.isShowByDefault()); + } + + public static void setAspectAvailability(LineAnnotationAspect aspect, boolean showByDefault) { + if (aspect.getId() == null) throw new IllegalArgumentException("Aspect should have not null ID"); + PropertiesComponent.getInstance().setValue(ANNO_ASPECT + aspect.getId(), String.valueOf(showByDefault)); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java index 8b1a75279420..88a025d5368c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java @@ -20,8 +20,12 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorGutterAction; import com.intellij.openapi.editor.colors.ColorKey; import com.intellij.openapi.editor.colors.EditorFontType; -import com.intellij.openapi.vcs.annotate.*; +import com.intellij.openapi.vcs.annotate.AnnotationListener; +import com.intellij.openapi.vcs.annotate.FileAnnotation; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; +import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.vcsUtil.VcsUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.Nullable; @@ -148,6 +152,6 @@ class AnnotationFieldGutter implements ActiveAnnotationGutter { } private boolean isAvailable() { - return myShowAdditionalInfo || myAspect instanceof MajorLineAnnotationAspect; + return myShowAdditionalInfo || VcsUtil.isAspectAvailableByDefault(myAspect); } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index 2f3360ed75dc..84b735c31f7f 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1789,3 +1789,4 @@ log.method.name=Log method name log.condition.text=Log condition text ignore.if.annotated.by=Ignore if annotated by choose.annotation.class=Choose annotation class +only.weaken.to.an.interface=Only weaken to an interface diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java index 3b8105cedc8b..65ce2308d392 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java @@ -48,6 +48,9 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { @SuppressWarnings({"PublicField"}) public boolean doNotWeakenToJavaLangObject = true; + @SuppressWarnings({"PublicField"}) + public boolean onlyWeakentoInterface = true; + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( @@ -100,6 +103,8 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { optionsPanel.addCheckbox(InspectionGadgetsBundle.message( "type.may.be.weakened.do.not.weaken.to.object.option"), "doNotWeakenToJavaLangObject"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("only.weaken.to.an.interface"), + "onlyWeakentoInterface"); return optionsPanel; } @@ -272,6 +277,15 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { variable.getResolveScope()); weakestClasses.remove(javaLangObjectClass); } + if (onlyWeakentoInterface) { + for (Iterator iterator = weakestClasses.iterator(); + iterator.hasNext();) { + final PsiClass weakestClass = iterator.next(); + if (!weakestClass.isInterface()) { + iterator.remove(); + } + } + } if (weakestClasses.isEmpty()) { return; } @@ -310,6 +324,15 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { method.getResolveScope()); weakestClasses.remove(javaLangObjectClass); } + if (onlyWeakentoInterface) { + for (Iterator iterator = weakestClasses.iterator(); + iterator.hasNext();) { + final PsiClass weakestClass = iterator.next(); + if (!weakestClass.isInterface()) { + iterator.remove(); + } + } + } if (weakestClasses.isEmpty()) { return; } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java index 86274d86bee2..5c9520cd4884 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java @@ -386,8 +386,14 @@ public class AndroidFacet extends Facet { } @Nullable - public static AndroidFacet getInstance(@NotNull PsiElement element) { - Module module = ModuleUtil.findModuleForPsiElement(element); + public static AndroidFacet getInstance(@NotNull final PsiElement element) { + Module module = ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + @Override + public Module compute() { + return ModuleUtil.findModuleForPsiElement(element); + } + }); if (module == null) return null; return getInstance(module); } diff --git a/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java b/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java index 7cf92a12ebcd..9f3abbfd0130 100644 --- a/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java +++ b/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java @@ -172,7 +172,8 @@ public class DeviceChooser extends DialogWrapper implements AndroidDebugBridge.I @NotNull private static String getDeviceState(@NotNull IDevice device) { - return capitalize(device.getState().name().toLowerCase()); + IDevice.DeviceState state = device.getState(); + return state != null ? capitalize(state.name().toLowerCase()) : ""; } @Override diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java index 7a3dc2e6d13d..4a699b857442 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java @@ -37,7 +37,7 @@ public class CvsFileAnnotation implements FileAnnotation{ private final VirtualFile myFile; private final List myListeners = new ArrayList(); - private final LineAnnotationAspect USER = new CvsUserAnnotationAspect() { + private final LineAnnotationAspect USER = new CvsAnnotationAspect(CvsAnnotationAspect.AUTHOR, true) { public String getValue(int lineNumber) { if (lineNumber < 0 || lineNumber >= myAnnotations.length) { return ""; @@ -48,7 +48,7 @@ public class CvsFileAnnotation implements FileAnnotation{ } }; - private final LineAnnotationAspect DATE = new CvsAnnotationAspect() { + private final LineAnnotationAspect DATE = new CvsAnnotationAspect(CvsAnnotationAspect.DATE, true) { public String getValue(int lineNumber) { if (lineNumber < 0 || lineNumber >= myAnnotations.length) { return ""; @@ -59,7 +59,7 @@ public class CvsFileAnnotation implements FileAnnotation{ } }; - private final LineAnnotationAspect REVISION = new CvsAnnotationAspect() { + private final LineAnnotationAspect REVISION = new CvsAnnotationAspect(CvsAnnotationAspect.REVISION, false) { public String getValue(int lineNumber) { if (lineNumber < 0 || lineNumber >= myAnnotations.length) { return ""; @@ -173,12 +173,13 @@ public class CvsFileAnnotation implements FileAnnotation{ } private abstract class CvsAnnotationAspect extends LineAnnotationAspectAdapter { + public CvsAnnotationAspect(String id, boolean showByDefault) { + super(id, showByDefault); + } + @Override protected void showAffectedPaths(int lineNum) { //todo } } - - private abstract class CvsUserAnnotationAspect extends CvsAnnotationAspect implements MajorLineAnnotationAspect { - } } diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index 3f01599ea6e3..197586c81b46 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -81,14 +81,14 @@ public class GitFileAnnotation implements FileAnnotation { */ private final boolean myMonitorFlag; - private final LineAnnotationAspect DATE_ASPECT = new GitAnnotationAspect() { + private final LineAnnotationAspect DATE_ASPECT = new GitAnnotationAspect(GitAnnotationAspect.DATE, true) { public String doGetValue(LineInfo info) { final Date date = info.getDate(); return date == null ? "" : DateFormatUtil.formatPrettyDate(date); } }; - private final LineAnnotationAspect REVISION_ASPECT = new GitAnnotationAspect() { + private final LineAnnotationAspect REVISION_ASPECT = new GitAnnotationAspect(GitAnnotationAspect.REVISION, false) { @Override protected String doGetValue(LineInfo lineInfo) { final GitRevisionNumber revision = lineInfo.getRevision(); @@ -96,7 +96,7 @@ public class GitFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect AUTHOR_ASPECT = new GitAuthorAnnotationAspect() { + private final LineAnnotationAspect AUTHOR_ASPECT = new GitAnnotationAspect(GitAnnotationAspect.AUTHOR, true) { @Override protected String doGetValue(LineInfo lineInfo) { final String author = lineInfo.getAuthor(); @@ -278,6 +278,10 @@ public class GitFileAnnotation implements FileAnnotation { * Revision annotation aspect implementation */ private abstract class GitAnnotationAspect extends LineAnnotationAspectAdapter { + public GitAnnotationAspect(String id, boolean showByDefault) { + super(id, showByDefault); + } + public String getValue(int lineNumber) { if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) { return ""; @@ -301,9 +305,6 @@ public class GitFileAnnotation implements FileAnnotation { } } - private abstract class GitAuthorAnnotationAspect extends GitAnnotationAspect implements MajorLineAnnotationAspect { - } - /** * Line information */ diff --git a/plugins/git4idea/src/git4idea/history/browser/GitCommit.java b/plugins/git4idea/src/git4idea/history/browser/GitCommit.java index 927f4c0ef946..bc6fbbc49605 100644 --- a/plugins/git4idea/src/git4idea/history/browser/GitCommit.java +++ b/plugins/git4idea/src/git4idea/history/browser/GitCommit.java @@ -140,7 +140,7 @@ public class GitCommit { return myAuthorEmail; } - public String getComitterEmail() { + public String getCommitterEmail() { return myComitterEmail; } diff --git a/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java b/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java index 0ee5ecfcfd0d..c64512dc9033 100644 --- a/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java +++ b/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java @@ -15,7 +15,6 @@ */ package git4idea.tests; -import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vfs.VirtualFile; import org.testng.annotations.BeforeMethod; @@ -38,7 +37,7 @@ import static org.testng.Assert.assertTrue; * TODO: This shouldn't rely on Git. Either test all version controls, either test a mock version control. Or even use both approaches. * @author Kirill Likhodedov */ -public class ChangeListManagerUpdateOnFileChangeTest extends GitTestCase { +public class ChangeListManagerUpdateOnFileChangeTest extends GitSingleUserTestCase { private ChangeListManagerImpl myChangeListManager; private VirtualFile afile; @@ -54,6 +53,7 @@ public class ChangeListManagerUpdateOnFileChangeTest extends GitTestCase { assertInChangeList(afile.getPath()); myRepo.commit("commit Message"); myChangeListManager.ensureUpToDate(false); + myRepo.refresh(); } @Test diff --git a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java index 6844fd67d6b4..a4a34b24cca1 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java @@ -23,13 +23,13 @@ import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.vcs.MockChangelistBuilder; +import com.intellij.ui.GuiUtils; import git4idea.GitVcs; import git4idea.changes.GitChangeProvider; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; -import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -45,7 +45,7 @@ import static org.testng.Assert.*; * 3. Calls ChangeProvider.getChanges() and checks that the changes are there. * @author Kirill Likhodedov */ -public class GitChangeProviderTest extends GitTestCase { +public class GitChangeProviderTest extends GitSingleUserTestCase { private GitChangeProvider myChangeProvider; private VcsModifiableDirtyScope myDirtyScope; @@ -91,9 +91,15 @@ public class GitChangeProviderTest extends GitTestCase { @Test public void testDeleteDirRecursively() throws Exception { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override public void run() { - FileUtil.delete(new File(myRepo.getDir().getPath(), "dir")); + GuiUtils.runOrInvokeAndWait(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + FileUtil.delete(new File(myRepo.getDir().getPath(), "dir")); + } + }); } }); assertChanges(new VirtualFile[] { myFiles.get("dir/c.txt"), myFiles.get("dir/subdir/d.txt") }, new FileStatus[] { DELETED, DELETED }); @@ -169,7 +175,7 @@ public class GitChangeProviderTest extends GitTestCase { assertChanges(newFile, FileStatus.MERGED_WITH_CONFLICTS); } - private void modifyFileInBranches(String filename, FileAction masterAction, FileAction featureAction) throws IOException { + private void modifyFileInBranches(String filename, FileAction masterAction, FileAction featureAction) throws Exception { myRepo.createBranch("feature"); performActionOnFileAndRecordToIndex(filename, "feature", featureAction); myRepo.commit(); @@ -184,7 +190,7 @@ public class GitChangeProviderTest extends GitTestCase { CREATE, MODIFY, DELETE, RENAME } - private void performActionOnFileAndRecordToIndex(String filename, String branchName, FileAction action) throws IOException { + private void performActionOnFileAndRecordToIndex(String filename, String branchName, FileAction action) throws Exception { VirtualFile file = myRepo.getDir().findChild(filename); switch (action) { case CREATE: diff --git a/plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java new file mode 100644 index 000000000000..4c21c4fa32ab --- /dev/null +++ b/plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2010 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 git4idea.tests; + +/** + * + * @author Kirill Likhodedov + */ +public class GitCollaborativeTestCase extends GitTestCase { + public static final String MAIN_USER_NAME = "John Smith"; + public static final String MAIN_USER_EMAIL = "john.smith@email.com"; + public static final String BROTHER_USER_NAME = "Bob Doe"; + public static final String BROTHER_USER_EMAIL = "bob.doe@email.com"; + + protected GitTestRepository myRepo; // main repository with IDEA project + protected GitTestRepository myParentRepo; // bare 'central' repository + protected GitTestRepository myBrotherRepo; // another developers repository + + @Override + protected GitTestRepository initRepositories() throws Exception { + myParentRepo = GitTestRepository.create(this); + myRepo = GitTestRepository.cloneFrom(myParentRepo); + myRepo.setName(MAIN_USER_NAME, MAIN_USER_EMAIL); + myBrotherRepo = GitTestRepository.cloneFrom(myParentRepo); + myBrotherRepo.setName(BROTHER_USER_NAME, BROTHER_USER_EMAIL); + return myRepo; + } + + @Override + protected void tearDownRepositories() throws Exception { + myRepo.getDirFixture().tearDown(); + myParentRepo.getDirFixture().tearDown(); + myBrotherRepo.getDirFixture().tearDown(); + } + +} diff --git a/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java index f2d00123616e..f9e449cfb6e8 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java +++ b/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java @@ -46,7 +46,7 @@ import static org.testng.Assert.*; * * @author Kirill Likhodedov */ -public class GitHistoryUtilsTestCase extends GitTestCase { +public class GitHistoryUtilsTestCase extends GitSingleUserTestCase { private VirtualFile afile; private FilePath bfilePath; @@ -124,7 +124,7 @@ public class GitHistoryUtilsTestCase extends GitTestCase { parents = ArrayUtil.EMPTY_STRING_ARRAY; } final GitTestRevision revision = new GitTestRevision(details[0], details[1], parents, commitMessages[i], - String.format("%s <%s>", CONFIG_USER_NAME, CONFIG_USER_EMAIL), null, + CONFIG_USER_NAME, CONFIG_USER_EMAIL, CONFIG_USER_NAME, CONFIG_USER_EMAIL, null, contents[i]); myRevisions.add(revision); if (i > RENAME_COMMIT_INDEX) { @@ -280,14 +280,17 @@ public class GitHistoryUtilsTestCase extends GitTestCase { assertEquals(((GitRevisionNumber) actual.getRevisionNumber()).getTimestamp(), expected.myDate); // TODO: whitespaces problem is known, remove convertWhitespaces... when it's fixed assertEquals(convertWhitespacesToSpacesAndRemoveDoubles(actual.getCommitMessage()), convertWhitespacesToSpacesAndRemoveDoubles(expected.myCommitMessage)); - assertEquals(actual.getAuthor(), expected.myAuthor); + assertEquals(actual.getAuthor(), expected.myAuthorName); assertEquals(actual.getBranchName(), expected.myBranchName); assertEquals(actual.getContent(), expected.myContent); } private static void assertCommitEqualToTestRevision(GitCommit commit, GitTestRevision expected) throws IOException { assertEquals(commit.getHash().toString(), expected.myHash); - assertEquals( String.format("%s <%s>", commit.getAuthor(), commit.getAuthorEmail()), expected.myAuthor); + assertEquals(commit.getAuthor(), expected.myAuthorName); + assertEquals(commit.getAuthorEmail(), expected.myAuthorEmail); + assertEquals(commit.getCommitter(), expected.myCommitterName); + assertEquals(commit.getCommitterEmail(), expected.myCommitterEmail); assertEquals(commit.getDate(), expected.myDate); assertEquals(convertWhitespacesToSpacesAndRemoveDoubles(commit.getDescription()), convertWhitespacesToSpacesAndRemoveDoubles(expected.myCommitMessage)); assertEqualHashes(commit.getParentsHashes(), Arrays.asList(expected.myParents)); @@ -317,17 +320,23 @@ public class GitHistoryUtilsTestCase extends GitTestCase { final String myHash; final Date myDate; final String myCommitMessage; - final String myAuthor; + final String myAuthorName; + final String myAuthorEmail; + final String myCommitterName; + final String myCommitterEmail; final String myBranchName; final byte[] myContent; private String[] myParents; - public GitTestRevision(String hash, String gitTimestamp, String[] parents, String commitMessage, String author, String branch, String content) { + public GitTestRevision(String hash, String gitTimestamp, String[] parents, String commitMessage, String authorName, String authorEmail, String committerName, String committerEmail, String branch, String content) { myHash = hash; myDate = gitTimeStampToDate(gitTimestamp); myParents = parents; myCommitMessage = commitMessage; - myAuthor = author; + myAuthorName = authorName; + myAuthorEmail = authorEmail; + myCommitterName = committerName; + myCommitterEmail = committerEmail; myBranchName = branch; myContent = content.getBytes(); } diff --git a/plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java new file mode 100644 index 000000000000..89e65d1aba36 --- /dev/null +++ b/plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2010 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 git4idea.tests; + +/** + * @author Kirill Likhodedov + */ +public class GitSingleUserTestCase extends GitTestCase { + public static final String CONFIG_USER_NAME = "John Smith"; + public static final String CONFIG_USER_EMAIL = "john.smith@email.com"; + + protected GitTestRepository myRepo; + + @Override + protected GitTestRepository initRepositories() throws Exception { + myRepo = GitTestRepository.create(this); + myRepo.setName(CONFIG_USER_NAME, CONFIG_USER_EMAIL); + return myRepo; + } + + @Override + protected void tearDownRepositories() throws Exception { + myRepo.getDirFixture().tearDown(); + } +} diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitTestCase.java index 3bf58e14122c..591aa22ef642 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestCase.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestCase.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.testFramework.AbstractVcsTestCase; +import com.intellij.ui.GuiUtils; import git4idea.GitVcs; import org.jetbrains.annotations.Nullable; import org.testng.annotations.AfterMethod; @@ -37,14 +38,12 @@ import java.io.IOException; * contains git executable. * @author Kirill Likhodedov */ -public class GitTestCase extends AbstractVcsTestCase { +public abstract class GitTestCase extends AbstractVcsTestCase { public static final String GIT_EXECUTABLE_PATH = "IDEA_TEST_GIT_EXECUTABLE_PATH"; - public static final String CONFIG_USER_NAME = "Git TestCase Name"; - public static final String CONFIG_USER_EMAIL = "Git TestCase Email"; private static final String GIT_EXECUTABLE = (SystemInfo.isWindows ? "git.exe" : "git"); - protected GitTestRepository myRepo; + protected GitTestRepository myMainRepo; private File myProjectDir; @BeforeMethod @@ -59,11 +58,9 @@ public class GitTestCase extends AbstractVcsTestCase { myClientBinaryPath = new File(pluginRoot, "tests/git4idea/tests/data/bin"); } - myRepo = GitTestRepository.create(this); - myRepo.config("user.name", CONFIG_USER_NAME); - myRepo.config("user.email", CONFIG_USER_EMAIL); - myProjectDir = new File(myRepo.getDirFixture().getTempDirPath()); + myMainRepo = initRepositories(); + myProjectDir = new File(myMainRepo.getDirFixture().getTempDirPath()); if (EventQueue.isDispatchThread()) { initProject(myProjectDir); } else { @@ -86,10 +83,28 @@ public class GitTestCase extends AbstractVcsTestCase { doActionSilently(VcsConfiguration.StandardConfirmation.REMOVE); } + /** + * Different implementations for {@link git4idea.tests.GitSingleUserTestCase} and {@link git4idea.tests.GitCollaborativeTestCase}: + * create a single or several repositories, which will be used in tests. + * @return main repository which IDEA project will be bound to. + */ + protected abstract GitTestRepository initRepositories() throws Exception; + + protected abstract void tearDownRepositories() throws Exception; + @AfterMethod protected void tearDown() throws Exception { - tearDownProject(); - myRepo.getDirFixture().tearDown(); + GuiUtils.runOrInvokeAndWait(new Runnable() { + @Override + public void run() { + try { + tearDownProject(); + tearDownRepositories(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); } /** diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java b/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java index 93297f60c279..ceb131bc2519 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java @@ -22,12 +22,16 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.AbstractVcsTestCase; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.ui.GuiUtils; +import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.concurrent.atomic.AtomicReference; +import static org.testng.Assert.fail; + /** * Representation of a Git repository for tests purposes. * @author Kirill Likhodedov @@ -35,13 +39,13 @@ import java.util.concurrent.atomic.AtomicReference; public class GitTestRepository { @NotNull private final GitTestCase myTest; @NotNull private final TempDirTestFixture myDirFixture; + private VirtualFile myDir; - public GitTestRepository(@NotNull GitTestCase test, @NotNull TempDirTestFixture dir) { + public GitTestRepository(@NotNull GitTestCase test, @NotNull TempDirTestFixture fixture) { myTest = test; - myDirFixture = dir; + myDirFixture = fixture; } - /** * Creates a new Mercurial repository in a new temporary test directory. * @param testCase reference to the test case instance. @@ -55,6 +59,14 @@ public class GitTestRepository { return new GitTestRepository(testCase, dirFixture); } + public static GitTestRepository cloneFrom(GitTestRepository parent) throws Exception { + final TempDirTestFixture dirFixture = createFixtureDir(); + final File repo = new File(dirFixture.getTempDirPath()); + final ProcessOutput processOutput = parent.getTest().executeCommand(repo, "clone", parent.getDir().getPath(), repo.getPath()); + AbstractVcsTestCase.verify(processOutput); + return new GitTestRepository(parent.getTest(), dirFixture); + } + private static TempDirTestFixture createFixtureDir() throws Exception { final TempDirTestFixture fixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture(); fixture.setUp(); @@ -66,8 +78,20 @@ public class GitTestRepository { return myDirFixture; } + @Nullable public VirtualFile getDir() { - return myDirFixture.getFile("."); + if (myDir == null) { + myDir = VcsUtil.getVirtualFile(myDirFixture.getTempDirPath()); + } + return myDir; + } + + /** + * Configures name and email for this git repository. + */ + public void setName(String name, String email) throws IOException { + config("user.name", name); + config("user.email", email); } /** @@ -175,15 +199,21 @@ public class GitTestRepository { execute(true, "mv", file.getPath(), newPath); } - public void mv(String oldPath, String newPath) throws IOException { + public void mv(String oldPath, String newPath) throws Exception { execute(true, "mv", oldPath, newPath); refreshFile(getDir(), true); } - private static void refreshFile(final VirtualFile file, final boolean recursive) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override public void run() { - file.refresh(false, recursive); + private static void refreshFile(final VirtualFile file, final boolean recursive) throws Exception { + GuiUtils.runOrInvokeAndWait(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + file.refresh(false, recursive); + } + }); } }); } @@ -196,7 +226,7 @@ public class GitTestRepository { execute(true, "push"); } - public void rm(String... filenames) throws IOException { + public void rm(String... filenames) throws Exception { execute(true, join("rm", filenames)); refreshFile(getDir(), true); } @@ -255,8 +285,28 @@ public class GitTestRepository { public void refresh() { ApplicationManager.getApplication().invokeAndWait(new Runnable() { public void run() { - refreshFile(getDir(), true); + try { + refreshFile(getDir(), true); + } catch (Exception e) { + e.printStackTrace(); + fail("Exception while refreshing repository", e); + } } }, ModalityState.defaultModalityState()); } + + public void addRemotes(String... parameters) throws IOException { + for (String s : parameters) { + execute(true, "remote", "add", s); + } + } + + public void branch() throws IOException { + execute(false, "branch"); + } + + @NotNull + public GitTestCase getTest() { + return myTest; + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java index 1cf807c422e4..02addf7afa86 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java @@ -105,10 +105,24 @@ public class HgAnnotation implements FileAnnotation { return result; } + private static String id(FIELD field) { + switch (field) { + case USER: return LineAnnotationAspect.AUTHOR; + case REVISION: return LineAnnotationAspect.REVISION; + case DATE: return LineAnnotationAspect.DATE; + default: return null; + } + } + + private static boolean isShowByDefault(FIELD aspectType) { + return aspectType == FIELD.DATE || aspectType == FIELD.USER; + } + class HgLineAnnotationAspect extends LineAnnotationAspectAdapter { private final FIELD aspectType; public HgLineAnnotationAspect(FIELD aspectType) { + super(id(aspectType), HgAnnotation.isShowByDefault(aspectType)); this.aspectType = aspectType; } @@ -128,12 +142,6 @@ public class HgAnnotation implements FileAnnotation { } } - class HgUserAnnotationAspect extends HgLineAnnotationAspect implements MajorLineAnnotationAspect { - public HgUserAnnotationAspect(FIELD aspectType) { - super(aspectType); - } - } - public boolean revisionsNotEmpty() { return true; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index 30d7b8d0d35f..8c710fa66bd8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -617,4 +617,5 @@ action.mark.tree.conflict.resolved.confirmation.title=Mark Tree Conflict Resolve svn.integrate.changelist.warning.unresolved.conflicts.text=Integration interrupted. There are unresolved conflicts or skipped items. svn.option.ignore.whitespace.in.annotate=Ignore whitespace differences in annotations annotation.show.merge.sources.default.text=Show merge source in annotations -svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no" \ No newline at end of file +svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no" +annotation.original.revision=Original revision \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java index c6697614bdae..d711a362dd29 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java @@ -23,6 +23,7 @@ import com.intellij.util.text.DateFormatUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnEntriesListener; import org.jetbrains.idea.svn.SvnVcs; @@ -40,7 +41,7 @@ public class SvnFileAnnotation implements FileAnnotation { private final List myListeners = new ArrayList(); private final Map myRevisionMap = new HashMap(); - private final LineAnnotationAspect DATE_ASPECT = new SvnAnnotationAspect() { + private final LineAnnotationAspect DATE_ASPECT = new SvnAnnotationAspect(SvnAnnotationAspect.DATE, true) { public String getValue(int lineNumber) { if (myInfos.size() <= lineNumber || lineNumber < 0) { return ""; @@ -52,7 +53,7 @@ public class SvnFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect REVISION_ASPECT = new SvnAnnotationAspect() { + private final LineAnnotationAspect REVISION_ASPECT = new SvnAnnotationAspect(SvnAnnotationAspect.REVISION, false) { public String getValue(int lineNumber) { if (myInfos.size() <= lineNumber || lineNumber < 0) { return ""; @@ -64,7 +65,7 @@ public class SvnFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect ORIGINAL_REVISION_ASPECT = new SvnAnnotationAspect() { + private final LineAnnotationAspect ORIGINAL_REVISION_ASPECT = new SvnAnnotationAspect(SvnBundle.message("annotation.original.revision"), false) { @Override public String getValue(int lineNumber) { final long value = myInfos.originalRevision(lineNumber); @@ -92,7 +93,7 @@ public class SvnFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect AUTHOR_ASPECT = new SvnAuthorAnnotationAspect() { + private final LineAnnotationAspect AUTHOR_ASPECT = new SvnAnnotationAspect(SvnAnnotationAspect.AUTHOR, true) { public String getValue(int lineNumber) { if (myInfos.size() <= lineNumber || lineNumber < 0) { return ""; @@ -290,6 +291,10 @@ public class SvnFileAnnotation implements FileAnnotation { } private abstract class SvnAnnotationAspect extends LineAnnotationAspectAdapter { + public SvnAnnotationAspect(String id, boolean showByDefault) { + super(id, showByDefault); + } + protected long getRevision(final int lineNum) { final LineInfo lineInfo = myInfos.get(lineNum); return (lineInfo == null) ? -1 : lineInfo.getRevision(); @@ -306,9 +311,6 @@ public class SvnFileAnnotation implements FileAnnotation { } } - private abstract class SvnAuthorAnnotationAspect extends SvnAnnotationAspect implements MajorLineAnnotationAspect { - } - private static class MyPartiallyCreatedInfos { private boolean myShowMergeSource; private final Map myMappedLineInfo; diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java index 45dbdf27768e..1aa4e3b06fa6 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java @@ -76,7 +76,9 @@ public class XmlHighlightVisitor extends XmlElementVisitor implements HighlightV @Override protected Boolean compute(PsiElement parent, Object p) { OuterLanguageElement element = PsiTreeUtil.getChildOfType(parent, OuterLanguageElement.class); - return element != null && element.getLanguage() != parent.getLanguage(); + if (element == null) return false; + PsiFile containingFile = parent.getContainingFile(); + return containingFile.getViewProvider().getBaseLanguage() != containingFile.getLanguage(); } }; private List myResult;