Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Oleg Shpynov
2010-10-28 19:49:55 +04:00
86 changed files with 2417 additions and 307 deletions
@@ -306,7 +306,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod {
final PsiCodeBlock body = method.getBody();
if (body == null) return;
final Collection<PsiClassType> exceptionTypes = ExceptionUtil.collectUnhandledExceptions(method, body);
final Collection<PsiClassType> exceptionTypes = ExceptionUtil.collectUnhandledExceptions(body, method);
for (final PsiClassType exceptionType : exceptionTypes) {
updateThrowsList(exceptionType);
}
@@ -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 {
@@ -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());
@@ -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);
}
}
}
@@ -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);
}
});
}
@@ -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<String>(Arrays.asList(suggestedName.names)), exprMarker);
.performInplaceRename(false, new LinkedHashSet<String>(Arrays.asList(suggestedName.names)), new Consumer<Boolean>(){
@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();
}
});
}
}
}
@@ -0,0 +1,19 @@
PsiJavaFile:ChainedClassObjectAccess.java
PsiReferenceExpression:A.class.B
PsiClassObjectAccessExpression:A.class
PsiTypeElement:A
PsiJavaCodeReferenceElement:A
PsiIdentifier:A('A')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiKeyword:class('class')
PsiJavaToken:DOT('.')
PsiReferenceParameterList
<empty list>
PsiIdentifier:B('B')
PsiJavaToken:DOT('.')
PsiErrorElement:Identifier expected
<empty list>
PsiErrorElement:Unparsed tokens
PsiKeyword:class('class')
@@ -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() {
@@ -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" +
"}"
);
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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");
@@ -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;
@@ -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");
}
}
}
@@ -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();
@@ -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<String> nameSuggestions, final RangeMarker offsetAfter) {
public boolean performInplaceRename(boolean processTextOccurrences, LinkedHashSet<String> nameSuggestions, final Consumer<Boolean> 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);
}
}
});
@@ -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();
}
@@ -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) {
@@ -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);
}
@@ -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) {
}
@@ -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);
}
}
@@ -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();
}
@@ -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();
@@ -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) {
}
}
}
}
@@ -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);
}
}
@@ -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<DockContainer> getContainers();
}
@@ -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> {
T getKey();
Image getPreviewImage();
String getDockContainerType();
Dimension getPreferredSize();
void close();
Presentation getPresentation();
}
@@ -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 {
}
@@ -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();
}
@@ -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);
}
}
@@ -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();
}
}
@@ -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<JBTabsImpl> NAVIGATION_ACTIONS_KEY = DataKey.create("JBTabs");
ActionManager myActionManager;
public final List<TabInfo> myVisibleInfos = new ArrayList<TabInfo>();
private final List<TabInfo> myVisibleInfos = new ArrayList<TabInfo>();
private final Map<TabInfo, Integer> myHiddenInfos = new HashMap<TabInfo, Integer>();
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<TabInfo> visible = new ArrayList<TabInfo>();
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();
}
}
@@ -21,6 +21,13 @@ import java.awt.*;
import java.util.List;
public abstract class LayoutPassInfo {
public java.util.List<TabInfo> myVisibleInfos;
protected LayoutPassInfo(List<TabInfo> visibleInfos) {
myVisibleInfos = visibleInfos;
}
public abstract TabInfo getPreviousFor(TabInfo info);
public abstract TabInfo getNextFor(TabInfo info);
@@ -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);
}
}
}
@@ -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);
}
@@ -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<TabInfo> 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;
}
}
@@ -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;
}
@@ -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<TabInfo> 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() {
@@ -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<TabInfo> 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<TabInfo> 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;
}
}
@@ -36,7 +36,8 @@ public class TablePassInfo extends LayoutPassInfo {
JBTabsImpl myTabs;
TablePassInfo(final JBTabsImpl tabs) {
TablePassInfo(final JBTabsImpl tabs, java.util.List<TabInfo> visibleInfos) {
super(visibleInfos);
myTabs = tabs;
}
@@ -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);
}
@@ -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);
}
});
}
}
@@ -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<ErrorStripeListener> myErrorMarkerListeners = new ArrayList<ErrorStripeListener>();
@@ -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);
@@ -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<EditorDataProvider> myDataProviders = new ArrayList<EditorDataProvider>();
@@ -71,6 +74,8 @@ public abstract class FileEditorManagerEx extends FileEditorManager {
*/
public abstract EditorWindow getCurrentWindow();
public abstract AsyncResult<EditorWindow> 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<EditorsSplitters> splitters = mgr.getAllSplitters();
for (Iterator<EditorsSplitters> iterator = splitters.iterator(); iterator.hasNext();) {
EditorsSplitters each = iterator.next();
for (VirtualFile file : mgr.getOpenFiles()) {
each.updateFileIcon(file);
}
}
}
}
@@ -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<DockableEditorTabbedContainer> container = new Ref<DockableEditorTabbedContainer>();
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() {
}
}
@@ -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<Listener> myListeners = new CopyOnWriteArraySet<Listener>();
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() {
}
}
@@ -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<ActionGroup>() {
@@ -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<EditorWindow> window = mgr.getActiveWindow();
window.doWhenDone(new AsyncResult.Handler<EditorWindow>() {
@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<VirtualFile> {
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;
}
}
}
}
@@ -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<EditorWithProviderComposite> editors = editorManager.getEditorComposites(file);
final List<EditorWithProviderComposite> 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;
@@ -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);
@@ -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<FileEditorManagerListener>(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<EditorsSplitters> getAllSplitters() {
HashSet<EditorsSplitters> all = new HashSet<EditorsSplitters>();
all.add(getMainSplitters());
Set<DockContainer> dockContainers = DockManager.getInstance(myProject).getContainers();
for (Iterator<DockContainer> iterator = dockContainers.iterator(); iterator.hasNext();) {
DockContainer each = iterator.next();
if (each instanceof DockableEditorTabbedContainer) {
all.add(((DockableEditorTabbedContainer)each).getSplitters());
}
}
return Collections.unmodifiableSet(all);
}
public AsyncResult<EditorsSplitters> getActiveSplitters() {
final AsyncResult<EditorsSplitters> result = new AsyncResult<EditorsSplitters>();
final IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
fm.doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
Component focusOwner = fm.getFocusOwner();
if (focusOwner != null) {
Set<EditorsSplitters> 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<EditorWindow> getActiveWindow() {
final AsyncResult<EditorWindow> result = new AsyncResult<EditorWindow>();
getActiveSplitters().doWhenDone(new AsyncResult.Handler<EditorsSplitters>() {
@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<EditorWindow> windows = getSplitters().findWindows(file);
final List<EditorWindow> 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<EditorWindow> windows = getSplitters().findWindows(file);
final List<EditorWindow> 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<FileEditor[], FileEditorProvider[]> openFileImpl2(@NotNull final EditorWindow window,
@NotNull public Pair<FileEditor[], FileEditorProvider[]> openFileImpl2(@NotNull final EditorWindow window,
@NotNull final VirtualFile file,
final boolean focusEditor) {
final Ref<Pair<FileEditor[], FileEditorProvider[]>> result = new Ref<Pair<FileEditor[], FileEditorProvider[]>>();
@@ -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<FileEditor, FileEditorProvider> selectedEditorWithProvider = getSelectedEditorWithProvider(file);
return selectedEditorWithProvider == null ? null : selectedEditorWithProvider.getFirst();
@@ -895,7 +970,12 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
@NotNull
public List<EditorWithProviderComposite> getEditorComposites(final VirtualFile file) {
return getSplitters().findEditorComposites(file);
ArrayList<EditorWithProviderComposite> result = new ArrayList<EditorWithProviderComposite>();
Set<EditorsSplitters> 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"
@@ -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) {
@@ -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<IdeFrame, WeakReference<Component>> myLastFocused = new HashMap<IdeFrame, WeakReference<Component>>();
private final Map<IdeFrame, WeakReference<Component>> myLastFocusedAtDeactivation = new HashMap<IdeFrame, WeakReference<Component>>();
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;
@@ -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();
}
}
@@ -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();
}
@@ -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;
}
@@ -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)) {
@@ -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);
}
});
}
@@ -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) {
@@ -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<Element>{
private Project myProject;
private Map<String, DockContainerFactory> myFactories = new HashMap<String, DockContainerFactory>();
private Set<DockContainer> myContainers = new HashSet<DockContainer>();
private Map<DockContainer, DockWindow> myWindows = new HashMap<DockContainer, DockWindow>();
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<DockContainer> 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);
}
}
}
@@ -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
@@ -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
@@ -161,6 +161,11 @@
<implementation-class>com.intellij.openapi.project.DumbModeIndicator</implementation-class>
</component>
<component>
<interface-class>com.intellij.ui.docking.DockManager</interface-class>
<implementation-class>com.intellij.ui.docking.impl.DockManagerImpl</implementation-class>
</component>
<component>
<implementation-class>com.intellij.notification.impl.ProjectNotificationsComponent</implementation-class>
<skipForDefaultProject/>
@@ -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<EditorWindow> getActiveWindow() {
return new AsyncResult.Done<EditorWindow>(null);
}
@Override
public void setCurrentWindow(EditorWindow window) {
}
@@ -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);
}
}
@@ -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 <code>null</code> this line aspect won't be configurable in annotation settings
*
* @return unique id
*/
@Nullable
String getId();
/**
* Returns <code>true</code> if this aspect will be shown on Annotate action
*
* @return <code>true</code> if this aspect will be shown on Annotate action
*/
boolean isShowByDefault();
}
@@ -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);
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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
@@ -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<PsiClass> 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<PsiClass> iterator = weakestClasses.iterator();
iterator.hasNext();) {
final PsiClass weakestClass = iterator.next();
if (!weakestClass.isInterface()) {
iterator.remove();
}
}
}
if (weakestClasses.isEmpty()) {
return;
}
@@ -386,8 +386,14 @@ public class AndroidFacet extends Facet<AndroidFacetConfiguration> {
}
@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<Module>() {
@Nullable
@Override
public Module compute() {
return ModuleUtil.findModuleForPsiElement(element);
}
});
if (module == null) return null;
return getInstance(module);
}
@@ -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
@@ -37,7 +37,7 @@ public class CvsFileAnnotation implements FileAnnotation{
private final VirtualFile myFile;
private final List<AnnotationListener> myListeners = new ArrayList<AnnotationListener>();
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 {
}
}
@@ -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
*/
@@ -140,7 +140,7 @@ public class GitCommit {
return myAuthorEmail;
}
public String getComitterEmail() {
public String getCommitterEmail() {
return myComitterEmail;
}
@@ -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
@@ -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:
@@ -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();
}
}
@@ -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();
}
@@ -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();
}
}
@@ -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();
}
}
});
}
/**
@@ -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;
}
}
@@ -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;
}
@@ -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"
svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no"
annotation.original.revision=Original revision
@@ -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<AnnotationListener> myListeners = new ArrayList<AnnotationListener>();
private final Map<Long, SvnFileRevision> myRevisionMap = new HashMap<Long, SvnFileRevision>();
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<Integer, LineInfo> myMappedLineInfo;
@@ -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<HighlightInfo> myResult;