From d4c1933bbc752dfc22044fe215f1005fb369c5c6 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 11 Oct 2010 15:24:00 +0400 Subject: [PATCH 01/36] editor detach: drag out handler --- .../openapi/util/registry/Registry.java | 4 ++ .../openapi/util/registry/RegistryValue.java | 9 +++ .../src/com/intellij/ui/MouseDragHelper.java | 24 ++++++- .../intellij/ui/docking/DockContainer.java | 25 +++++++ .../com/intellij/ui/docking/DockManager.java | 30 ++++++++ .../intellij/ui/docking/DockableContent.java | 19 +++++ .../src/com/intellij/ui/tabs/JBTabs.java | 2 + .../src/com/intellij/ui/tabs/TabInfo.java | 23 ++++++ .../com/intellij/ui/tabs/impl/DragHelper.java | 20 ++++++ .../com/intellij/ui/tabs/impl/JBTabsImpl.java | 7 +- .../com/intellij/ui/tabs/impl/TabLayout.java | 10 +++ .../tabs/impl/singleRow/SingleRowLayout.java | 5 ++ .../singleRow/SingleRowLayoutStrategy.java | 14 ++++ .../impl/EditorTabbedContainer.java | 23 +++++- .../ui/docking/impl/DockManagerImpl.java | 71 +++++++++++++++++++ .../src/misc/registry.properties | 1 + .../src/componentSets/Platform.xml | 5 ++ 17 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/docking/DockContainer.java create mode 100644 platform/platform-api/src/com/intellij/ui/docking/DockManager.java create mode 100644 platform/platform-api/src/com/intellij/ui/docking/DockableContent.java create mode 100644 platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java diff --git a/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java b/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java index 5d9f169dda42..20666f5013c9 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java +++ b/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java @@ -60,6 +60,10 @@ public class Registry { return get(key).asInteger(); } + public static double doubleValue(@PropertyKey(resourceBundle = REGISTRY_BUNDLE) String key) { + return get(key).asDouble(); + } + public static String stringValue(@PropertyKey(resourceBundle = REGISTRY_BUNDLE) String key) { return get(key).asString(); } diff --git a/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java b/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java index b791891f4b69..f653548634bf 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java +++ b/platform/platform-api/src/com/intellij/openapi/util/registry/RegistryValue.java @@ -37,6 +37,7 @@ public class RegistryValue { private String myStringCachedValue; private Integer myIntCachedValue; + private Double myDoubleCachedValue; private Boolean myBooleanCachedValue; RegistryValue(Registry registry, String key) { @@ -71,6 +72,14 @@ public class RegistryValue { return myIntCachedValue.intValue(); } + public double asDouble() { + if (myDoubleCachedValue == null) { + myDoubleCachedValue = Double.valueOf(get(myKey, "0.0", true)); + } + + return myDoubleCachedValue.doubleValue(); + } + public Color asColor(Color defaultValue) { final String s = get(myKey, null, true); if (s != null) { diff --git a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java index e15e44b177c0..d892c2c70214 100644 --- a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java @@ -45,6 +45,8 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe private final Disposable myParentDisposable; private Dimension myDelta; + private boolean myDetachingMode; + public MouseDragHelper(Disposable parent, final JComponent dragComponent) { myDragComponent = dragComponent; myParentDisposable = parent; @@ -117,6 +119,7 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe myDraggingNow = false; myPressPointComponent = null; myPressPointScreen = null; + myDetachingMode = false; e.consume(); } } @@ -141,7 +144,19 @@ 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; + dragOutStarted = true; + } + } + + if (myDetachingMode) { + processDragOut(e, draggedTo, (Point)myPressPointScreen.clone(), dragOutStarted); + } else { + processDrag(e, draggedTo, (Point)myPressPointScreen.clone()); + } e.consume(); } @@ -170,6 +185,13 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe protected abstract void processDrag(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint); + protected boolean isDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint) { + return false; + } + + protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) { + + } private boolean isWithinDeadZone(final MouseEvent e) { final Point screen = new RelativePoint(e).getScreenPoint(); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java new file mode 100644 index 000000000000..a8cf601b1220 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -0,0 +1,25 @@ +/* + * 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.ui.awt.RelativeRectangle; + +public interface DockContainer { + + RelativeRectangle getAcceptArea(); + boolean canAccept(DockableContent content); + +} diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java new file mode 100644 index 000000000000..802dcd465ec2 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -0,0 +1,30 @@ +/* + * 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.ProjectComponent; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; + +public abstract class DockManager implements ProjectComponent { + + public abstract void register(DockContainer container, @Nullable Disposable parent); + + public static DockManager getInstance(Project project) { + return project.getComponent(DockManager.class); + } +} diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java new file mode 100644 index 000000000000..9e518d08cf5f --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -0,0 +1,19 @@ +/* + * 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; + +public interface DockableContent { +} diff --git a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java index e5f352b4d1e7..359e0dd3198d 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java @@ -116,4 +116,6 @@ public interface JBTabs extends SwitchProvider { boolean isDisposed(); JBTabs setAdditinalSwitchProviderWhenOriginal(SwitchProvider delegate); + + Image getComponentImage(TabInfo info); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java index 63eeb5c50303..ec5e96c72169 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/TabInfo.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeSupport; import java.lang.ref.WeakReference; import java.util.Map; @@ -81,6 +82,7 @@ public final class TabInfo implements Queryable { private Color myTabColor = null; private Queryable myQueryable; + private DragOutDelegate myDragOutDelegate; public TabInfo(final JComponent component) { myComponent = component; @@ -353,4 +355,25 @@ public final class TabInfo implements Queryable { myQueryable.putInfo(info); } } + + public TabInfo setDragOutDelegate(DragOutDelegate delegate) { + myDragOutDelegate = delegate; + return this; + } + + public boolean canBeDraggedOut() { + return myDragOutDelegate != null; + } + + public DragOutDelegate getDragOutDelegate() { + return myDragOutDelegate; + } + + public interface DragOutDelegate { + + void dragOutStarted(MouseEvent mouseEvent, TabInfo info); + void processDragOut(MouseEvent event, TabInfo source); + void dragOutFinished(MouseEvent event, TabInfo source); + } + } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java index 9b22e29565ae..91d95da4afba 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java @@ -42,6 +42,26 @@ class DragHelper extends MouseDragHelper { 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 = myDragSource.getDragOutDelegate(); + if (justStarted) { + delegate.dragOutStarted(event, myDragSource); + } else { + delegate.processDragOut(event, myDragSource); + } + } + protected void processDrag(MouseEvent event, Point targetScreenPoint, Point startPointScreen) { if (!myTabs.isTabDraggingEnabled()) return; diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 3df5f186e975..1799e952e5a2 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -297,6 +297,11 @@ public class JBTabsImpl extends JComponent return this; } + @Override + public Image getComponentImage(TabInfo info) { + return new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB); + } + public void dispose() { myDisposed = true; mySelectedInfo = null; @@ -2593,7 +2598,7 @@ public class JBTabsImpl extends JComponent return !myHorizontalSide; } - private TabLayout getEffectiveLayout() { + public TabLayout getEffectiveLayout() { if (myLayout == myTableLayout && getTabsPosition() == JBTabsPosition.top) return myTableLayout; return mySingleRowLayout; } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java index 8022a4457d0a..2cee099bc399 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java @@ -15,6 +15,8 @@ */ package com.intellij.ui.tabs.impl; +import com.intellij.openapi.util.registry.Registry; + import java.awt.*; public abstract class TabLayout { @@ -28,8 +30,16 @@ 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"); + } + } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java index 876b06f794e2..1b62b9792654 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java @@ -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); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java index 7b7810998a0e..2ebaa704d5ed 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayoutStrategy.java @@ -17,6 +17,8 @@ package com.intellij.ui.tabs.impl.singleRow; import com.intellij.ui.tabs.impl.JBTabsImpl; import com.intellij.ui.tabs.impl.ShapeTransform; +import com.intellij.ui.tabs.impl.TabLabel; +import com.intellij.ui.tabs.impl.table.TableLayout; import java.awt.*; @@ -62,6 +64,8 @@ public abstract class SingleRowLayoutStrategy { return false; } + public abstract boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY); + abstract static class Horizontal extends SingleRowLayoutStrategy { protected Horizontal(final SingleRowLayout layout) { super(layout); @@ -76,6 +80,11 @@ public abstract class SingleRowLayoutStrategy { return true; } + @Override + public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) { + return Math.abs(deltaY) > tabLabel.getHeight() * TableLayout.getDragOutMultiplier(); + } + public int getMoreRectAxisSize() { return myLayout.myMoreIcon.getIconWidth() + 6; } @@ -204,6 +213,11 @@ public abstract class SingleRowLayoutStrategy { super(layout); } + @Override + public boolean isDragOut(TabLabel tabLabel, int deltaX, int deltaY) { + return Math.abs(deltaX) > tabLabel.getHeight() * TableLayout.getDragOutMultiplier(); + } + public boolean isToCenterTextWhenStretched() { return false; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index d6dc383d19f9..4dcc25800c9e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -68,6 +68,8 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget @NonNls public static final String HELP_ID = "ideaInterface.editor"; + private TabInfo.DragOutDelegate myDragOutDelegate = new MyDragOutDelegate(); + EditorTabbedContainer(final EditorWindow window, Project project, int tabPlacement) { myWindow = window; myProject = project; @@ -267,7 +269,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(); @@ -474,4 +477,22 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget return false; } } + + private class MyDragOutDelegate implements TabInfo.DragOutDelegate { + @Override + public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { + Image img = myTabs.getComponentImage(info); + + VirtualFile file = (VirtualFile)info.getObject(); + FileEditorManagerEx.getInstanceEx(myProject).closeFile(file, myWindow); + } + + @Override + public void processDragOut(MouseEvent event, TabInfo source) { + } + + @Override + public void dragOutFinished(MouseEvent event, TabInfo source) { + } + } } diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java new file mode 100644 index 000000000000..3696bc47abc9 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -0,0 +1,71 @@ +/* + * 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.openapi.Disposable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; + +public class DockManagerImpl extends DockManager { + + private Project myProject; + + private Set myContainers = new HashSet(); + + public DockManagerImpl(Project project) { + myProject = project; + } + + @Override + public void register(final DockContainer container, @Nullable Disposable parent) { + myContainers.add(container); + Disposer.register(parent != null ? parent : myProject, new Disposable() { + @Override + public void dispose() { + myContainers.remove(container); + } + }); + } + + @Override + public void projectOpened() { + } + + @Override + public void projectClosed() { + } + + @NotNull + @Override + public String getComponentName() { + return "DockManager"; + } + + @Override + public void initComponent() { + } + + @Override + public void disposeComponent() { + } +} diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 48dfb12ded9d..0b30f59fd51e 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -73,6 +73,7 @@ ide.tooltip.autoDismissDeadZone=300 ide.tabbedPane.bufferedPaint=true +ide.tabbedPane.dragOutMultiplier=1.2 ide.use.native.mac.filechooser=false ide.mac.filechooser.showhidden.files=false diff --git a/platform/platform-resources/src/componentSets/Platform.xml b/platform/platform-resources/src/componentSets/Platform.xml index 324ecb5423cb..876c61add48b 100644 --- a/platform/platform-resources/src/componentSets/Platform.xml +++ b/platform/platform-resources/src/componentSets/Platform.xml @@ -156,6 +156,11 @@ com.intellij.openapi.project.DumbModeIndicator + + com.intellij.ui.docking.DockManager + com.intellij.ui.docking.impl.DockManagerImpl + + com.intellij.notification.impl.ProjectNotificationsComponent From a985771a0f83d45f0bd18de1dd99f3fd7f3f467d Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 11 Oct 2010 18:37:53 +0400 Subject: [PATCH 02/36] editor detach: drag out image --- .../src/com/intellij/ui/MouseDragHelper.java | 13 ++- .../com/intellij/ui/docking/DockManager.java | 4 + .../intellij/ui/docking/DockableContent.java | 4 + .../com/intellij/ui/docking/DragSession.java | 24 +++++ .../com/intellij/ui/tabs/impl/DragHelper.java | 25 +++++- .../com/intellij/ui/tabs/impl/JBTabsImpl.java | 12 ++- .../impl/EditorTabbedContainer.java | 25 +++++- .../ui/docking/impl/DockManagerImpl.java | 89 +++++++++++++++++++ 8 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/docking/DragSession.java diff --git a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java index d892c2c70214..a444ca5e8e8c 100644 --- a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java @@ -113,7 +113,11 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe if (wasDragging) { try { - processDragFinish(e); + if (myDetachingMode) { + processDragOutFinish(e); + } else { + processDragFinish(e, false); + } } finally { myDraggingNow = false; @@ -148,6 +152,7 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe if (!myDetachingMode) { if (isDragOut(e, draggedTo, (Point)myPressPointScreen.clone())) { myDetachingMode = true; + processDragFinish(e, true); dragOutStarted = true; } } @@ -176,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() { diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java index 802dcd465ec2..31fab27b5bce 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -20,6 +20,8 @@ import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.Nullable; +import java.awt.event.MouseEvent; + public abstract class DockManager implements ProjectComponent { public abstract void register(DockContainer container, @Nullable Disposable parent); @@ -27,4 +29,6 @@ public abstract class DockManager implements ProjectComponent { public static DockManager getInstance(Project project) { return project.getComponent(DockManager.class); } + + public abstract DragSession createDragSession(MouseEvent mouseEvent, DockableContent content); } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java index 9e518d08cf5f..f572965fed10 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -15,5 +15,9 @@ */ package com.intellij.ui.docking; +import java.awt.*; + public interface DockableContent { + + Image getPreviewImage(); } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DragSession.java b/platform/platform-api/src/com/intellij/ui/docking/DragSession.java new file mode 100644 index 000000000000..1704cebf8574 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DragSession.java @@ -0,0 +1,24 @@ +/* + * 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 java.awt.event.MouseEvent; + +public interface DragSession { + + void process(MouseEvent e); + +} diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java index 91d95da4afba..e999913a23e9 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java @@ -37,6 +37,8 @@ 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; @@ -54,14 +56,21 @@ class DragHelper extends MouseDragHelper { @Override protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) { - TabInfo.DragOutDelegate delegate = myDragSource.getDragOutDelegate(); + TabInfo.DragOutDelegate delegate = myDragOutSource.getDragOutDelegate(); if (justStarted) { - delegate.dragOutStarted(event, myDragSource); + delegate.dragOutStarted(event, myDragOutSource); } else { - delegate.processDragOut(event, myDragSource); + 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; @@ -224,11 +233,19 @@ class DragHelper extends MouseDragHelper { } @Override - protected void processDragFinish(MouseEvent even) { + protected void processDragFinish(MouseEvent event, boolean willDragOutStart) { + super.processDragFinish(event, willDragOutStart); + + if (willDragOutStart) { + myDragOutSource = myDragSource; + } + myDragSource = null; myDragRec = null; myTabs.resetTabsCache(); myTabs.relayout(true, false); + + myTabs.revalidate(); } } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 1799e952e5a2..5049fc80158c 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -299,7 +299,17 @@ public class JBTabsImpl extends JComponent @Override public Image getComponentImage(TabInfo info) { - return new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB); + 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() { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 4dcc25800c9e..e1158b0318e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -38,6 +38,9 @@ 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.DockManager; +import com.intellij.ui.docking.DockableContent; +import com.intellij.ui.docking.DragSession; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; import com.intellij.ui.tabs.*; @@ -479,20 +482,40 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } private class MyDragOutDelegate implements TabInfo.DragOutDelegate { + + private VirtualFile myFile; + private DragSession mySession; + @Override public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { - Image img = myTabs.getComponentImage(info); + final Image img = myTabs.getComponentImage(info); VirtualFile file = (VirtualFile)info.getObject(); FileEditorManagerEx.getInstanceEx(myProject).closeFile(file, myWindow); + + mySession = getDockManager().createDragSession(mouseEvent, new DockableContent() { + @Override + public Image getPreviewImage() { + return img; + } + }); + } + + 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) { + mySession.process(event); + + myFile = null; + mySession = null; } } } diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 3696bc47abc9..0f585791b22e 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -18,11 +18,20 @@ package com.intellij.ui.docking.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.ui.docking.DockContainer; import com.intellij.ui.docking.DockManager; +import com.intellij.ui.docking.DockableContent; +import com.intellij.ui.docking.DragSession; 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.MouseEvent; +import java.awt.image.BufferedImage; import java.util.HashSet; import java.util.Set; @@ -31,6 +40,7 @@ public class DockManagerImpl extends DockManager { private Project myProject; private Set myContainers = new HashSet(); + private MyDragSession myCurrentDragSession; public DockManagerImpl(Project project) { myProject = project; @@ -47,6 +57,21 @@ public class DockManagerImpl extends DockManager { }); } + @Override + public DragSession createDragSession(MouseEvent mouseEvent, DockableContent content) { + cancelCurrentDragSession(); + myCurrentDragSession = new MyDragSession(mouseEvent, content); + return myCurrentDragSession; + } + + + private void cancelCurrentDragSession() { + if (myCurrentDragSession != null) { + myCurrentDragSession.cancel(); + myCurrentDragSession = null; + } + } + @Override public void projectOpened() { } @@ -68,4 +93,68 @@ public class DockManagerImpl extends DockManager { @Override public void disposeComponent() { } + + private class MyDragSession implements DragSession { + + private JWindow myWindow; + private Image myThumbnail; + + private MyDragSession(MouseEvent me, DockableContent content) { + myWindow = new JWindow(); + + 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); + + myThumbnail = buffer.getScaledInstance((int)(width * ratio), (int)(height * ratio), Image.SCALE_SMOOTH); + + myWindow.getContentPane().setLayout(new BorderLayout()); + JLabel label = new JLabel(new ImageIcon(myThumbnail)); + label.setBorder(new LineBorder(Color.lightGray)); + myWindow.getContentPane().add(label, 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 -= myThumbnail.getWidth(null) / 2; + showPoint.y += 10; + myWindow.setBounds(new Rectangle(showPoint, new Dimension(myThumbnail.getWidth(null), myThumbnail.getHeight(null)))); + } + + @Override + public void process(MouseEvent e) { + if (e.getID() == MouseEvent.MOUSE_DRAGGED) { + setLocationFrom(e); + } else if (e.getID() == MouseEvent.MOUSE_RELEASED) { + cancelCurrentDragSession(); + } + } + + public void cancel() { + myWindow.dispose(); + } + } } From 045b01e095825b0949787f1d3e0b0118e18f3aa0 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 11 Oct 2010 21:23:55 +0400 Subject: [PATCH 03/36] editor detach: drag out editor --- .../intellij/ui/docking/DockContainer.java | 6 ++ .../ui/docking/DockContainerFactory.java | 22 +++++ .../intellij/ui/docking/DockableContent.java | 4 + .../impl/EditorTabbedContainer.java | 80 ++++++++++++++++--- .../fileEditor/impl/EditorsSplitters.java | 2 +- .../impl/FileEditorManagerImpl.java | 2 +- .../ui/docking/impl/DockManagerImpl.java | 55 ++++++++++++- 7 files changed, 154 insertions(+), 17 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index a8cf601b1220..26f498824354 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -15,11 +15,17 @@ */ package com.intellij.ui.docking; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; +import javax.swing.*; + public interface DockContainer { RelativeRectangle getAcceptArea(); boolean canAccept(DockableContent content); + JComponent getComponent(); + + void add(DockableContent content, RelativePoint dropTarget); } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java new file mode 100644 index 000000000000..a7ed507e6f34 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java @@ -0,0 +1,22 @@ +/* + * 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; + +public interface DockContainerFactory { + + DockContainer createContainer(); + +} diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java index f572965fed10..b1229c238d54 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -20,4 +20,8 @@ import java.awt.*; public interface DockableContent { Image getPreviewImage(); + + DockContainerFactory getContainerFactory(); + + Dimension getPreferredSize(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index e1158b0318e9..93110d9bbede 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -38,9 +38,9 @@ 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.DockManager; -import com.intellij.ui.docking.DockableContent; -import com.intellij.ui.docking.DragSession; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.awt.RelativeRectangle; +import com.intellij.ui.docking.*; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; import com.intellij.ui.tabs.*; @@ -490,15 +490,10 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { final Image img = myTabs.getComponentImage(info); - VirtualFile file = (VirtualFile)info.getObject(); - FileEditorManagerEx.getInstanceEx(myProject).closeFile(file, myWindow); + myFile = (VirtualFile)info.getObject(); + FileEditorManagerEx.getInstanceEx(myProject).closeFile(myFile, myWindow); - mySession = getDockManager().createDragSession(mouseEvent, new DockableContent() { - @Override - public Image getPreviewImage() { - return img; - } - }); + mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile)); } private DockManager getDockManager() { @@ -517,5 +512,68 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget myFile = null; mySession = null; } + + private class DockableEditor implements DockableContent { + private final Image myImg; + + public DockableEditor(Image img, VirtualFile file) { + myImg = img; + myFile = file; + } + + @Override + public Image getPreviewImage() { + return myImg; + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(myImg.getWidth(null), myImg.getHeight(null)); + } + + @Override + public DockContainerFactory getContainerFactory() { + return new MyFactory(); + } + + public VirtualFile getFile() { + return myFile; + } + } } + + private class MyFactory implements DockContainerFactory, DockContainer { + + private EditorsSplitters mySplitters; + + @Override + public DockContainer createContainer() { + mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)); + mySplitters.createCurrentWindow(); + return this; + } + + @Override + public RelativeRectangle getAcceptArea() { + return new RelativeRectangle(mySplitters); + } + + @Override + public boolean canAccept(DockableContent content) { + return content instanceof MyDragOutDelegate.DockableEditor; + } + + @Override + public void add(DockableContent content, RelativePoint dropTarget) { + VirtualFile file = ((MyDragOutDelegate.DockableEditor)content).getFile(); + EditorWindow currentWindow = mySplitters.getCurrentWindow(); + ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(currentWindow, file, true); + } + + @Override + public JComponent getComponent() { + return mySplitters; + } + } + } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index 3780227dd82f..bccb356dc48c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -473,7 +473,7 @@ public final class EditorsSplitters extends JPanel { return getCurrentWindow(); } - private void createCurrentWindow() { + public void createCurrentWindow() { LOG.assertTrue(myCurrentWindow == null); setCurrentWindow(new EditorWindow(this)); add(myCurrentWindow.myPanel, BorderLayout.CENTER); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 9678febdb2e2..553052d79108 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -463,7 +463,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return openFileImpl2(getSplitters().getOrCreateCurrentWindow(file), file, focusEditor); } - @NotNull Pair openFileImpl2(@NotNull final EditorWindow window, + @NotNull public Pair openFileImpl2(@NotNull final EditorWindow window, @NotNull final VirtualFile file, final boolean focusEditor) { final Ref> result = new Ref>(); diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 0f585791b22e..622e11ff1f7b 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -17,9 +17,10 @@ package com.intellij.ui.docking.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.WindowManagerEx; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.docking.DockContainer; import com.intellij.ui.docking.DockManager; import com.intellij.ui.docking.DockableContent; @@ -59,13 +60,13 @@ public class DockManagerImpl extends DockManager { @Override public DragSession createDragSession(MouseEvent mouseEvent, DockableContent content) { - cancelCurrentDragSession(); + stopCurrentDragSession(); myCurrentDragSession = new MyDragSession(mouseEvent, content); return myCurrentDragSession; } - private void cancelCurrentDragSession() { + private void stopCurrentDragSession() { if (myCurrentDragSession != null) { myCurrentDragSession.cancel(); myCurrentDragSession = null; @@ -98,9 +99,11 @@ public class DockManagerImpl extends DockManager { private JWindow myWindow; private Image myThumbnail; + private DockableContent myContent; private MyDragSession(MouseEvent me, DockableContent content) { myWindow = new JWindow(); + myContent = content; Image previewImage = content.getPreviewImage(); @@ -149,7 +152,8 @@ public class DockManagerImpl extends DockManager { if (e.getID() == MouseEvent.MOUSE_DRAGGED) { setLocationFrom(e); } else if (e.getID() == MouseEvent.MOUSE_RELEASED) { - cancelCurrentDragSession(); + createNewDockContainerFor(myContent, e); + stopCurrentDragSession(); } } @@ -157,4 +161,47 @@ public class DockManagerImpl extends DockManager { myWindow.dispose(); } } + + private void createNewDockContainerFor(DockableContent content, MouseEvent event) { + DockContainer container = content.getContainerFactory().createContainer(); + register(container, null); + DockWindow window = new DockWindow(myProject, container); + window.show(); + + Dimension size = content.getPreferredSize(); + Point showPoint = event.getPoint(); + SwingUtilities.convertPointToScreen(showPoint, event.getComponent()); + showPoint.x -= size.width / 2; + showPoint.y += size.width / 2; + + window.setLocation(showPoint); + + window.show(); + + window.setSize(size.width, size.height); + container.add(content, new RelativePoint(event)); + } + + private class DockWindow extends DialogWrapper { + + private DockContainer myContainer; + + private DockWindow(Project project, DockContainer container) { + super(project); + myContainer = container; + setModal(false); + setUndecorated(false); + init(); + } + + @Override + protected Action[] createActions() { + return new Action[0]; + } + + @Override + protected JComponent createCenterPanel() { + return myContainer.getComponent(); + } + } } From 57ae7a5bb413cd1f0bedb6161045e39bef5a471b Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 12 Oct 2010 14:16:09 +0400 Subject: [PATCH 04/36] editor detach: interm checkin --- .../com/intellij/openapi/ui/FrameWrapper.java | 8 +++++++ .../ui/docking/impl/DockManagerImpl.java | 21 +++++-------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java index 6acd547148b3..3c1b7e8c76f3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java @@ -261,6 +261,14 @@ public class FrameWrapper implements Disposable { } } + public void setLocation(Point location) { + getFrame().setLocation(location); + } + + public void setSize(Dimension size) { + getFrame().setSize(size); + } + private class MyProjectManagerListener extends ProjectManagerAdapter { public void projectClosing(Project project) { if (project == myProject) { diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 622e11ff1f7b..b43302368c97 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -18,6 +18,7 @@ package com.intellij.ui.docking.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.FrameWrapper; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.ui.awt.RelativePoint; @@ -178,30 +179,18 @@ public class DockManagerImpl extends DockManager { window.show(); - window.setSize(size.width, size.height); + window.setSize(size); container.add(content, new RelativePoint(event)); } - private class DockWindow extends DialogWrapper { + private class DockWindow extends FrameWrapper { private DockContainer myContainer; private DockWindow(Project project, DockContainer container) { - super(project); myContainer = container; - setModal(false); - setUndecorated(false); - init(); - } - - @Override - protected Action[] createActions() { - return new Action[0]; - } - - @Override - protected JComponent createCenterPanel() { - return myContainer.getComponent(); + setProject(project); + setComponent(myContainer.getComponent()); } } } From 06cd944a21b81bededefc00215fae20f81656b7e Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Thu, 14 Oct 2010 20:26:11 +0400 Subject: [PATCH 05/36] editor detach: basic tab actions got to work --- .../intellij/openapi/wm/IdeFocusManager.java | 5 +- .../wm/PassThroughtIdeFocusManager.java | 11 ++ .../intellij/ui/docking/DockContainer.java | 22 +++ .../com/intellij/ui/docking/DockManager.java | 3 + .../intellij/ui/docking/DockableContent.java | 6 +- .../ide/actions/CloseEditorAction.java | 17 ++- .../actionSystem/impl/ActionMenuItem.java | 32 ++-- .../fileEditor/ex/FileEditorManagerEx.java | 3 + .../impl/DockableEditorTabbedContainer.java | 126 ++++++++++++++++ .../impl/EditorTabbedContainer.java | 79 ++++------ .../openapi/fileEditor/impl/EditorWindow.java | 8 +- .../fileEditor/impl/EditorsSplitters.java | 9 +- .../impl/FileEditorManagerImpl.java | 137 +++++++++++++----- .../com/intellij/openapi/ui/FrameWrapper.java | 8 +- .../openapi/wm/impl/FocusManagerImpl.java | 35 +++++ .../wm/impl/IdeFocusManagerHeadless.java | 11 ++ .../openapi/wm/impl/IdeFocusManagerImpl.java | 11 ++ .../openapi/wm/impl/WindowManagerImpl.java | 2 + .../ui/docking/impl/DockManagerImpl.java | 57 +++++++- .../testFramework/TestEditorManagerImpl.java | 6 + 20 files changed, 477 insertions(+), 111 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java diff --git a/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java b/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java index 70e0d82ce4d1..9334907d5005 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/IdeFocusManager.java @@ -131,8 +131,7 @@ public abstract class IdeFocusManager implements FocusRequestor { return fm; } - public Component getFocusOwner() { - return isFocusBeingTransferred() ? null : KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - } + public abstract Component getFocusOwner(); + public abstract void runOnOwnContext(DataContext context, Runnable runnable); } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java b/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java index eb96491daae1..2bcfd182d812 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/PassThroughtIdeFocusManager.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; import org.jetbrains.annotations.NotNull; @@ -86,6 +87,16 @@ public class PassThroughtIdeFocusManager extends IdeFocusManager { return this; } + @Override + public Component getFocusOwner() { + return KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + runnable.run(); + } + @Override public void suspendKeyProcessingUntil(@NotNull ActionCallback done) { } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index 26f498824354..a30af6a019fb 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -15,6 +15,7 @@ */ package com.intellij.ui.docking; +import com.intellij.openapi.Disposable; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; @@ -28,4 +29,25 @@ public interface DockContainer { JComponent getComponent(); void add(DockableContent content, RelativePoint dropTarget); + + void closeAll(); + + void addListener(Listener listener, Disposable parent); + + boolean isEmpty(); + + interface Listener { + void contentAdded(Object key); + void contentRemoved(Object key); + + class Adapter implements Listener { + @Override + public void contentAdded(Object key) { + } + + @Override + public void contentRemoved(Object key) { + } + } + } } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java index 31fab27b5bce..2033b4a0398f 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -21,6 +21,7 @@ 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 { @@ -31,4 +32,6 @@ public abstract class DockManager implements ProjectComponent { } public abstract DragSession createDragSession(MouseEvent mouseEvent, DockableContent content); + + public abstract Set getContainers(); } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java index b1229c238d54..50d82ac7e7f2 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -17,11 +17,15 @@ package com.intellij.ui.docking; import java.awt.*; -public interface DockableContent { +public interface DockableContent { + + T getKey(); Image getPreviewImage(); DockContainerFactory getContainerFactory(); Dimension getPreferredSize(); + + void close(); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java index ba95dc4ac083..81d83dcc3ab5 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CloseEditorAction.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.EditorWindow; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -29,12 +30,14 @@ public class CloseEditorAction extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { final Project project = e.getData(PlatformDataKeys.PROJECT); - final FileEditorManagerEx editorManager = ((FileEditorManagerEx)FileEditorManager.getInstance(project)); + final FileEditorManagerImpl editorManager = getEditorManager(project); EditorWindow window = e.getData(EditorWindow.DATA_KEY); - VirtualFile file; + VirtualFile file = null; if (window == null) { - window = editorManager.getCurrentWindow(); - file = window.getSelectedFile(); + window = editorManager.getActiveWindow().getResult(); + if (window != null) { + file = window.getSelectedFile(); + } } else { file = e.getData(PlatformDataKeys.VIRTUAL_FILE); @@ -44,6 +47,10 @@ public class CloseEditorAction extends AnAction implements DumbAware { } } + private FileEditorManagerImpl getEditorManager(Project project) { + return ((FileEditorManagerImpl)FileEditorManager.getInstance(project)); + } + public void update(final AnActionEvent event){ final Presentation presentation = event.getPresentation(); final Project project = event.getData(PlatformDataKeys.PROJECT); @@ -56,7 +63,7 @@ public class CloseEditorAction extends AnAction implements DumbAware { } EditorWindow window = event.getData(EditorWindow.DATA_KEY); if (window == null) { - window = ((FileEditorManagerEx)FileEditorManager.getInstance(project)).getCurrentWindow(); + window = getEditorManager(project).getActiveWindow().getResult(); } presentation.setEnabled(window != null && window.getTabCount() > 0); } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java index 52e453b70a1c..c71097fa529e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java @@ -20,11 +20,14 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.ex.ActionUtil; +import com.intellij.openapi.actionSystem.ex.DataConstantsEx; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.SizedIcon; import com.intellij.ui.plaf.beg.BegMenuItemUI; import com.intellij.util.Icons; @@ -207,19 +210,24 @@ public class ActionMenuItem extends JMenuItem { } public void actionPerformed(final ActionEvent e) { - AnActionEvent event = new AnActionEvent( - new MouseEvent(ActionMenuItem.this, MouseEvent.MOUSE_PRESSED, 0, e.getModifiers(), getWidth() / 2, getHeight() / 2, 1, false), - myContext, myPlace, myPresentation, ActionManager.getInstance(), e.getModifiers()); - if (ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) { - ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); - actionManager.fireBeforeActionPerformed(myAction, myContext, event); - Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(event.getDataContext()); - if (component != null && !isInTree(component)) { - return; + IdeFocusManager.findInstanceByContext(myContext).runOnOwnContext(myContext, new Runnable() { + @Override + public void run() { + AnActionEvent event = new AnActionEvent( + new MouseEvent(ActionMenuItem.this, MouseEvent.MOUSE_PRESSED, 0, e.getModifiers(), getWidth() / 2, getHeight() / 2, 1, false), + myContext, myPlace, myPresentation, ActionManager.getInstance(), e.getModifiers()); + if (ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) { + ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); + actionManager.fireBeforeActionPerformed(myAction, myContext, event); + Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(event.getDataContext()); + if (component != null && !isInTree(component)) { + return; + } + myAction.actionPerformed(event); + actionManager.queueActionPerformedEvent(myAction, myContext, event); + } } - myAction.actionPerformed(event); - actionManager.queueActionPerformedEvent(myAction, myContext, event); - } + }); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java index d9ef702d7dfa..904cf4e4a398 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java @@ -24,6 +24,7 @@ import com.intellij.openapi.fileEditor.FileEditorProvider; import com.intellij.openapi.fileEditor.impl.EditorComposite; import com.intellij.openapi.fileEditor.impl.EditorWindow; 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; @@ -69,6 +70,8 @@ public abstract class FileEditorManagerEx extends FileEditorManager { */ public abstract EditorWindow getCurrentWindow(); + public abstract AsyncResult getActiveWindow(); + public abstract void setCurrentWindow(EditorWindow window); /** diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java new file mode 100644 index 000000000000..b244cd120516 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -0,0 +1,126 @@ +/* + * 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.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.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.DockableContent; + +import javax.swing.*; +import java.util.concurrent.CopyOnWriteArraySet; + +class DockableEditorTabbedContainer implements DockContainerFactory, DockContainer { + + private EditorsSplitters mySplitters; + private Project myProject; + + private CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); + + DockableEditorTabbedContainer(Project project) { + myProject = project; + } + + @Override + public DockContainer createContainer() { + mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)) { + @Override + protected void afterFileClosed(VirtualFile file) { + fireContentClosed(file); + } + + @Override + protected void afterFileOpen(VirtualFile file) { + fireContentOpen(file); + } + }; + mySplitters.createCurrentWindow(); + return this; + } + + private void fireContentClosed(VirtualFile file) { + for (Listener each : myListeners) { + each.contentRemoved(file); + } + } + + private 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) { + return content instanceof EditorTabbedContainer.MyDragOutDelegate.DockableEditor; + } + + @Override + public void add(DockableContent content, RelativePoint dropTarget) { + VirtualFile file = ((EditorTabbedContainer.MyDragOutDelegate.DockableEditor)content).getFile(); + EditorWindow currentWindow = mySplitters.getCurrentWindow(); + ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(currentWindow, file, true); + } + + @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.getOpenFiles().length == 0; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 93110d9bbede..80c6c3cf9776 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -38,8 +38,6 @@ 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.awt.RelativePoint; -import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.docking.*; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; @@ -396,14 +394,20 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget TabInfo selected = myTabs.getSelectedInfo(); if (selected == null) return; - VirtualFile file = (VirtualFile)selected.getObject(); + final VirtualFile file = (VirtualFile)selected.getObject(); final FileEditorManagerEx mgr = FileEditorManagerEx.getInstanceEx(myProject); - EditorWindow wnd = mgr.getCurrentWindow(); - if (wnd != null) { - if (wnd.findFileComposite(file) != null) { - mgr.closeFile(file, wnd); + + AsyncResult window = mgr.getActiveWindow(); + window.doWhenDone(new AsyncResult.Handler() { + @Override + public void run(EditorWindow wnd) { + if (wnd != null) { + if (wnd.findFileComposite(file) != null) { + mgr.closeFile(file, wnd); + } + } } - } + }); } private class TabMouseListener extends MouseAdapter { @@ -481,7 +485,7 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } } - private class MyDragOutDelegate implements TabInfo.DragOutDelegate { + class MyDragOutDelegate implements TabInfo.DragOutDelegate { private VirtualFile myFile; private DragSession mySession; @@ -491,9 +495,9 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget final Image img = myTabs.getComponentImage(info); myFile = (VirtualFile)info.getObject(); - FileEditorManagerEx.getInstanceEx(myProject).closeFile(myFile, myWindow); - mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile)); + + FileEditorManagerEx.getInstanceEx(myProject).closeFile(myFile, myWindow); } private DockManager getDockManager() { @@ -513,12 +517,20 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget mySession = null; } - private class DockableEditor implements DockableContent { - private final Image myImg; + class DockableEditor implements DockableContent { + final Image myImg; + private DockableEditorTabbedContainer myContainer; + public DockableEditor(Image img, VirtualFile file) { myImg = img; myFile = file; + myContainer = new DockableEditorTabbedContainer(myProject); + } + + @Override + public VirtualFile getKey() { + return myFile; } @Override @@ -533,7 +545,12 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget @Override public DockContainerFactory getContainerFactory() { - return new MyFactory(); + return myContainer; + } + + @Override + public void close() { + myContainer.close(myFile); } public VirtualFile getFile() { @@ -542,38 +559,4 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } } - private class MyFactory implements DockContainerFactory, DockContainer { - - private EditorsSplitters mySplitters; - - @Override - public DockContainer createContainer() { - mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)); - mySplitters.createCurrentWindow(); - return this; - } - - @Override - public RelativeRectangle getAcceptArea() { - return new RelativeRectangle(mySplitters); - } - - @Override - public boolean canAccept(DockableContent content) { - return content instanceof MyDragOutDelegate.DockableEditor; - } - - @Override - public void add(DockableContent content, RelativePoint dropTarget) { - VirtualFile file = ((MyDragOutDelegate.DockableEditor)content).getFile(); - EditorWindow currentWindow = mySplitters.getCurrentWindow(); - ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(currentWindow, file, true); - } - - @Override - public JComponent getComponent() { - return mySplitters; - } - } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 8d6bdec5de79..e77e66db92e2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -131,7 +131,7 @@ public class EditorWindow { editorManager.runChange(new Runnable() { public void run() { try { - final List editors = editorManager.getEditorComposites(file); + final List editors = myOwner.findEditorComposites(file); if (editors.isEmpty()) return; final EditorWithProviderComposite editor = findFileComposite(file); @@ -170,6 +170,8 @@ public class EditorWindow { editorManager.getProject().getMessageBus().syncPublisher(FileEditorManagerListener.FILE_EDITOR_MANAGER); afterPublisher.fileClosed(editorManager, file); + + myOwner.afterFileClosed(file); } } }); @@ -301,6 +303,10 @@ public class EditorWindow { } } + public EditorsSplitters getOwner() { + return myOwner; + } + protected static class TComp extends JPanel implements DataProvider{ final EditorWithProviderComposite myEditor; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index bccb356dc48c..b5da321ab1af 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -22,6 +22,7 @@ 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.InvalidDataException; @@ -50,7 +51,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; @@ -430,6 +431,12 @@ public final class EditorsSplitters extends JPanel { return 0; } + protected void afterFileClosed(VirtualFile file) { + } + + protected void afterFileOpen(VirtualFile file) { + } + private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy { public final Component getDefaultComponentImpl(final Container focusCycleRoot) { if (myCurrentWindow != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 553052d79108..bf214b93ac49 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -53,12 +53,15 @@ import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; import com.intellij.openapi.vfs.*; +import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.openapi.wm.impl.FrameTitleBuilder; import com.intellij.openapi.wm.impl.IdeFrameImpl; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockManager; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.impl.MessageListenerList; @@ -74,8 +77,7 @@ import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; -import java.util.ArrayList; -import java.util.Collection; +import java.util.*; import java.util.List; /** @@ -119,11 +121,50 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return myPanels; } - public EditorsSplitters getSplitters() { + public EditorsSplitters getMainSplitters() { initUI(); + return mySplitters; } + public Set getAllSplitters() { + HashSet all = new HashSet(); + all.add(getMainSplitters()); + Set dockContainers = DockManager.getInstance(myProject).getContainers(); + for (Iterator iterator = dockContainers.iterator(); iterator.hasNext();) { + DockContainer each = iterator.next(); + if (each instanceof DockableEditorTabbedContainer) { + all.add(((DockableEditorTabbedContainer)each).getSplitters()); + } + } + + return Collections.unmodifiableSet(all); + } + + public AsyncResult getActiveSplitters() { + final AsyncResult result = new AsyncResult(); + final IdeFocusManager fm = IdeFocusManager.getInstance(myProject); + fm.doWhenFocusSettlesDown(new Runnable() { + @Override + public void run() { + Component focusOwner = fm.getFocusOwner(); + + if (focusOwner != null) { + Set splitters = getAllSplitters(); + for (EditorsSplitters each : splitters) { + if (each == focusOwner || SwingUtilities.isDescendingFrom(focusOwner, each)) { + result.setDone(each); + return; + } + } + } + + result.setDone(getMainSplitters()); + } + }); + return result; + } + private final Object myInitLock = new Object(); private void initUI() { if (myPanels == null) { @@ -157,7 +198,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 +243,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 +255,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 +273,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 +292,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 +307,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 +327,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 +344,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 +352,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 +389,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 +399,27 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } public VirtualFile getCurrentFile() { - return getSplitters().getCurrentFile(); + return getMainSplitters().getCurrentFile(); + } + + public AsyncResult getActiveWindow() { + final AsyncResult result = new AsyncResult(); + getActiveSplitters().doWhenDone(new AsyncResult.Handler() { + @Override + public void run(EditorsSplitters editorsSplitters) { + result.setDone(editorsSplitters.getCurrentWindow()); + } + }); + + return result; } public EditorWindow getCurrentWindow() { - return getSplitters().getCurrentWindow(); + return getActiveWindow().getResult(); } public void setCurrentWindow(final EditorWindow window) { - getSplitters().setCurrentWindow(window, true); + getMainSplitters().setCurrentWindow(window, true); } public void closeFile(@NotNull final VirtualFile file, @NotNull final EditorWindow window) { @@ -376,7 +429,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public void run() { if (window.isFileOpen(file)) { window.closeFile(file); - final List windows = getSplitters().findWindows(file); + final List windows = window.getOwner().findWindows(file); if (windows.isEmpty()) { // no more windows containing this file left final LocalFileSystem.WatchRequest request = file.getUserData(WATCH_REQUEST_KEY); if (request != null) { @@ -425,9 +478,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private void closeFileImpl(@NotNull final VirtualFile file, final boolean moveFocus) { assertDispatchThread(); - getSplitters().runChange(new Runnable() { + getMainSplitters().runChange(new Runnable() { public void run() { - final List windows = getSplitters().findWindows(file); + final List windows = getMainSplitters().findWindows(file); if (!windows.isEmpty()) { final VirtualFile nextFile = findNextFile(file); for (final EditorWindow window : windows) { @@ -460,7 +513,7 @@ 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 public Pair openFileImpl2(@NotNull final EditorWindow window, @@ -600,7 +653,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 +843,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 +874,11 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return getSplitters().getSelectedEditors(); } + private EditorsSplitters getSplitters() { + EditorsSplitters active = getActiveSplitters().getResult(); + return active == null ? getMainSplitters() : active; + } + public FileEditor getSelectedEditor(@NotNull final VirtualFile file) { final Pair selectedEditorWithProvider = getSelectedEditorWithProvider(file); return selectedEditorWithProvider == null ? null : selectedEditorWithProvider.getFirst(); @@ -895,7 +955,12 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @NotNull public List getEditorComposites(final VirtualFile file) { - return getSplitters().findEditorComposites(file); + ArrayList result = new ArrayList(); + Set all = getAllSplitters(); + for (EditorsSplitters each : all) { + result.addAll(each.findEditorComposites(file)); + } + return result; } @NotNull @@ -964,7 +1029,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 +1062,7 @@ 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(); LaterInvocator.invokeLater(new Runnable() { public void run() { long currentTime = System.nanoTime(); @@ -1019,7 +1084,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. @@ -1040,11 +1105,11 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec //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 +1422,15 @@ private final class MyVirtualFileListener extends VirtualFileAdapter { public void uiSettingsChanged(final UISettings source) { assertDispatchThread(); setTabsMode(source.EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); - getSplitters().setTabsPlacement(source.EDITOR_TAB_PLACEMENT); - getSplitters().trimToSize(source.EDITOR_TAB_LIMIT); + getMainSplitters().setTabsPlacement(source.EDITOR_TAB_PLACEMENT); + getMainSplitters().trimToSize(source.EDITOR_TAB_LIMIT); // Tab layout policy if (source.SCROLL_TAB_LAYOUT_IN_EDITOR) { - getSplitters().setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + getMainSplitters().setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); } else { - getSplitters().setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); + getMainSplitters().setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); } // "Mark modified files with asterisk" diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java index 3c1b7e8c76f3..fea4eec2abff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java @@ -30,6 +30,7 @@ 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; @@ -154,11 +155,15 @@ public class FrameWrapper implements Disposable { assert !isDisposed : "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 +231,7 @@ public class FrameWrapper implements Disposable { private boolean myDisposing; private MyJFrame() throws HeadlessException { + setGlassPane(new IdeGlassPaneImpl(getRootPane())); } public void dispose() { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index 5d2ba5e31596..372a41f760ee 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -17,6 +17,8 @@ package com.intellij.openapi.wm.impl; import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationAdapter; import com.intellij.openapi.application.ApplicationManager; @@ -87,6 +89,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { private final Map> myLastFocused = new HashMap>(); private final Map> myLastFocusedAtDeactivation = new HashMap>(); + private DataContext myRunContext; + public FocusManagerImpl(WindowManager wm) { myApp = ApplicationManager.getApplication(); myQueue = IdeEventQueue.getInstance(); @@ -340,6 +344,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); @@ -417,6 +426,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } public boolean isFocusTransferReady() { + if (myRunContext != null) return true; + invalidateFocusRequestsQueue(); if (!myFocusRequests.isEmpty()) return false; @@ -499,6 +510,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 IdeFocusManager myManager; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java index e0f0a30ccaa5..ded95f259b16 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerHeadless.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm.impl; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; import com.intellij.openapi.wm.FocusCommand; @@ -87,4 +88,14 @@ public class IdeFocusManagerHeadless extends IdeFocusManager { public FocusRequestor getFurtherRequestor() { return this; } + + @Override + public Component getFocusOwner() { + return null; + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + runnable.run(); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java index ebb8754eb51e..1d97067c18a7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFocusManagerImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.wm.impl; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Expirable; import com.intellij.openapi.wm.FocusCommand; @@ -86,6 +87,16 @@ public class IdeFocusManagerImpl extends IdeFocusManager { return getGlobalInstance().getFurtherRequestor(); } + @Override + public Component getFocusOwner() { + return getGlobalInstance().getFocusOwner(); + } + + @Override + public void runOnOwnContext(DataContext context, Runnable runnable) { + getGlobalInstance().runOnOwnContext(context, runnable); + } + public boolean isFocusBeingTransferred() { return getGlobalInstance().isFocusBeingTransferred(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 350be8fb8329..d68077e8046e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -273,6 +273,8 @@ public final class WindowManagerImpl extends WindowManagerEx implements Applicat ((JWindow)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } else if (window instanceof JDialog) { ((JDialog)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); + } else if (window instanceof JFrame) { + ((JFrame)window).getRootPane().putClientProperty("Window.alpha", 1.0f - ratio); } } else if (AWTUtilitiesWrapper.isTranslucencySupported(AWTUtilitiesWrapper.TRANSLUCENT)) { diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index b43302368c97..d802595e3530 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -17,8 +17,9 @@ package com.intellij.ui.docking.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; 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; @@ -33,7 +34,10 @@ import javax.swing.*; import javax.swing.border.LineBorder; import java.awt.*; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -44,6 +48,13 @@ public class DockManagerImpl extends DockManager { private Set myContainers = new HashSet(); private MyDragSession myCurrentDragSession; + private BusyObject.Impl myBusyObject = new BusyObject.Impl() { + @Override + protected boolean isReady() { + return myCurrentDragSession == null; + } + }; + public DockManagerImpl(Project project) { myProject = project; } @@ -59,6 +70,11 @@ public class DockManagerImpl extends DockManager { }); } + @Override + public Set getContainers() { + return Collections.unmodifiableSet(myContainers); + } + @Override public DragSession createDragSession(MouseEvent mouseEvent, DockableContent content) { stopCurrentDragSession(); @@ -71,9 +87,14 @@ public class DockManagerImpl extends DockManager { if (myCurrentDragSession != null) { myCurrentDragSession.cancel(); myCurrentDragSession = null; + myBusyObject.onReady(); } } + private ActionCallback getReady() { + return myBusyObject.getReady(this); + } + @Override public void projectOpened() { } @@ -116,7 +137,8 @@ public class DockManagerImpl extends DockManager { double ratio; if (width > height) { ratio = requiredSize / width; - } else { + } + else { ratio = requiredSize / height; } @@ -152,7 +174,8 @@ public class DockManagerImpl extends DockManager { public void process(MouseEvent e) { if (e.getID() == MouseEvent.MOUSE_DRAGGED) { setLocationFrom(e); - } else if (e.getID() == MouseEvent.MOUSE_RELEASED) { + } + else if (e.getID() == MouseEvent.MOUSE_RELEASED) { createNewDockContainerFor(myContent, e); stopCurrentDragSession(); } @@ -191,6 +214,34 @@ public class DockManagerImpl extends DockManager { myContainer = container; setProject(project); setComponent(myContainer.getComponent()); + myContainer.addListener(new DockContainer.Listener.Adapter() { + @Override + public void contentRemoved(Object key) { + if (myContainer.isEmpty()) { + WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true); + WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 1f); + + getReady().doWhenDone(new Runnable() { + @Override + public void run() { + close(); + } + }); + } + } + }, this); + } + + @Override + protected JFrame createJFrame() { + JFrame frame = super.createJFrame(); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + myContainer.closeAll(); + } + }); + return frame; } } } diff --git a/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java b/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java index 08b08f3f4d08..cd58cd75fd4b 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestEditorManagerImpl.java @@ -32,6 +32,7 @@ import com.intellij.openapi.fileEditor.impl.EditorWindow; import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -208,6 +209,11 @@ import java.util.Map; return null; } + @Override + public AsyncResult getActiveWindow() { + return new AsyncResult.Done(null); + } + @Override public void setCurrentWindow(EditorWindow window) { } From 1065fcaecb1c2eac7001c6ebdfcb2e06fac06585 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 18 Oct 2010 13:46:39 +0400 Subject: [PATCH 06/36] compliation fix after merge --- .../openapi/fileEditor/ex/FileEditorManagerEx.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java index 4f927935c22f..6b2644bf6f91 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java @@ -35,7 +35,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Set; public abstract class FileEditorManagerEx extends FileEditorManager { protected final List myDataProviders = new ArrayList(); @@ -155,9 +157,12 @@ public abstract class FileEditorManagerEx extends FileEditorManager { public void refreshIcons() { if (this instanceof FileEditorManagerImpl) { final FileEditorManagerImpl mgr = (FileEditorManagerImpl)this; - final EditorsSplitters splitters = mgr.getSplitters(); - for (VirtualFile file : mgr.getOpenFiles()) { - splitters.updateFileIcon(file); + Set splitters = mgr.getAllSplitters(); + for (Iterator iterator = splitters.iterator(); iterator.hasNext();) { + EditorsSplitters each = iterator.next(); + for (VirtualFile file : mgr.getOpenFiles()) { + each.updateFileIcon(file); + } } } } From 6418af5d1cc58c9e74e99913f6e069fe43377655 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 18 Oct 2010 17:11:03 +0400 Subject: [PATCH 07/36] editor detach: intermediate commit --- .../intellij/ui/docking/DockContainer.java | 7 +- .../intellij/ui/docking/DockableContent.java | 4 ++ .../src/com/intellij/ui/tabs/JBTabs.java | 5 ++ .../com/intellij/ui/tabs/impl/JBTabsImpl.java | 13 ++++ .../impl/DockableEditorTabbedContainer.java | 69 +++++++++++++++---- .../impl/EditorTabbedContainer.java | 18 +++-- .../fileEditor/impl/EditorsSplitters.java | 20 ++++++ .../ui/docking/impl/DockManagerImpl.java | 51 +++++++++++--- 8 files changed, 161 insertions(+), 26 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index a30af6a019fb..02ad0ca8ada4 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -24,7 +24,7 @@ import javax.swing.*; public interface DockContainer { RelativeRectangle getAcceptArea(); - boolean canAccept(DockableContent content); + boolean canAccept(DockableContent content, RelativePoint point); JComponent getComponent(); @@ -36,6 +36,11 @@ public interface DockContainer { boolean isEmpty(); + void startDropOver(DockableContent content, RelativePoint point); + void processDropOver(DockableContent content, RelativePoint point); + void resetDropOver(DockableContent content); + + interface Listener { void contentAdded(Object key); void contentRemoved(Object key); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java index 50d82ac7e7f2..f82f799f1676 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -15,6 +15,8 @@ */ package com.intellij.ui.docking; +import com.intellij.openapi.actionSystem.Presentation; + import java.awt.*; public interface DockableContent { @@ -28,4 +30,6 @@ public interface DockableContent { Dimension getPreferredSize(); void close(); + + Presentation getPresentation(); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java index 359e0dd3198d..2e171b01fd53 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java @@ -19,6 +19,7 @@ import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Getter; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; import org.jetbrains.annotations.NotNull; @@ -118,4 +119,8 @@ public interface JBTabs extends SwitchProvider { JBTabs setAdditinalSwitchProviderWhenOriginal(SwitchProvider delegate); Image getComponentImage(TabInfo info); + + void resetDropOver(TabInfo tabInfo); + void startDropOver(TabInfo tabInfo, RelativePoint point); + void processDropOver(TabInfo over, RelativePoint point); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 5049fc80158c..f4b66c5c232e 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -29,6 +29,7 @@ import com.intellij.openapi.wm.IdeGlassPane; import com.intellij.openapi.wm.IdeGlassPaneUtil; import com.intellij.openapi.wm.impl.content.GraphicsConfig; import com.intellij.ui.CaptionPanel; +import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.switcher.QuickActionProvider; import com.intellij.ui.switcher.SwitchProvider; @@ -2920,4 +2921,16 @@ public class JBTabsImpl extends JComponent return new Object[] {myInfo}; } } + + @Override + public void resetDropOver(TabInfo tabInfo) { + } + + @Override + public void startDropOver(TabInfo tabInfo, RelativePoint point) { + } + + @Override + public void processDropOver(TabInfo over, RelativePoint point) { + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index b244cd120516..42f6b6e1c1d6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -16,6 +16,7 @@ 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; @@ -26,6 +27,8 @@ import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.docking.DockContainer; import com.intellij.ui.docking.DockContainerFactory; import com.intellij.ui.docking.DockableContent; +import com.intellij.ui.tabs.JBTabs; +import com.intellij.ui.tabs.TabInfo; import javax.swing.*; import java.util.concurrent.CopyOnWriteArraySet; @@ -37,24 +40,34 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain private CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); + private JBTabs myCurrentOver; + private TabInfo myCurrentOverInfo; + DockableEditorTabbedContainer(Project project) { myProject = project; } + DockableEditorTabbedContainer(Project project, EditorsSplitters splitters) { + myProject = project; + mySplitters = splitters; + } + @Override public DockContainer createContainer() { - mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)) { - @Override - protected void afterFileClosed(VirtualFile file) { - fireContentClosed(file); - } + if (mySplitters == null) { + mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)) { + @Override + protected void afterFileClosed(VirtualFile file) { + fireContentClosed(file); + } - @Override - protected void afterFileOpen(VirtualFile file) { - fireContentOpen(file); - } - }; - mySplitters.createCurrentWindow(); + @Override + protected void afterFileOpen(VirtualFile file) { + fireContentOpen(file); + } + }; + mySplitters.createCurrentWindow(); + } return this; } @@ -76,7 +89,7 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain } @Override - public boolean canAccept(DockableContent content) { + public boolean canAccept(DockableContent content, RelativePoint point) { return content instanceof EditorTabbedContainer.MyDragOutDelegate.DockableEditor; } @@ -87,6 +100,38 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(currentWindow, file, true); } + @Override + public void startDropOver(DockableContent content, RelativePoint point) { + } + + @Override + public void 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(null).setText(presentation.getText()).setIcon(presentation.getIcon()); + myCurrentOver.startDropOver(myCurrentOverInfo, point); + } + + if (myCurrentOver != null) { + myCurrentOver.processDropOver(myCurrentOverInfo, point); + } + } + + @Override + public void resetDropOver(DockableContent content) { + if (myCurrentOver != null) { + myCurrentOver.resetDropOver(myCurrentOverInfo); + myCurrentOver = null; + myCurrentOverInfo = null; + } + } + @Override public JComponent getComponent() { return mySplitters; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 80c6c3cf9776..e2c5721b2a09 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -493,11 +493,12 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget @Override public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { final Image img = myTabs.getComponentImage(info); + info.setHidden(true); myFile = (VirtualFile)info.getObject(); - mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile)); - - FileEditorManagerEx.getInstanceEx(myProject).closeFile(myFile, myWindow); + Presentation presentation = new Presentation(info.getText()); + presentation.setIcon(info.getIcon()); + mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile, presentation)); } private DockManager getDockManager() { @@ -511,6 +512,8 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget @Override public void dragOutFinished(MouseEvent event, TabInfo source) { + FileEditorManagerEx.getInstanceEx(myProject).closeFile(myFile, myWindow); + mySession.process(event); myFile = null; @@ -520,11 +523,13 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget class DockableEditor implements DockableContent { final Image myImg; private DockableEditorTabbedContainer myContainer; + private Presentation myPresentation; - public DockableEditor(Image img, VirtualFile file) { + public DockableEditor(Image img, VirtualFile file, Presentation presentation) { myImg = img; myFile = file; + myPresentation = presentation; myContainer = new DockableEditorTabbedContainer(myProject); } @@ -548,6 +553,11 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget return myContainer; } + @Override + public Presentation getPresentation() { + return myPresentation; + } + @Override public void close() { myContainer.close(myFile); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index 79a48c0a7513..930c707687ec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -31,11 +31,15 @@ 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; @@ -71,6 +75,9 @@ public class EditorsSplitters extends JPanel { setFocusTraversalPolicy(new MyFocusTraversalPolicy()); setTransferHandler(new MyTransferHandler()); clear(); + + DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), this); + DockManager.getInstance(myManager.getProject()).register(dockable, myManager.getProject()); } public FileEditorManagerImpl getManager() { @@ -437,6 +444,19 @@ public class EditorsSplitters extends JPanel { 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; + } + private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy { public final Component getDefaultComponentImpl(final Container focusCycleRoot) { if (myCurrentWindow != null) { diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index d802595e3530..d3bd5b8a6274 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -23,6 +23,7 @@ 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.DockContainer; import com.intellij.ui.docking.DockManager; import com.intellij.ui.docking.DockableContent; @@ -123,6 +124,8 @@ public class DockManagerImpl extends DockManager { private Image myThumbnail; private DockableContent myContent; + private DockContainer myCurrentOverContainer; + private MyDragSession(MouseEvent me, DockableContent content) { myWindow = new JWindow(); myContent = content; @@ -172,12 +175,33 @@ public class DockManagerImpl extends DockManager { @Override public void process(MouseEvent e) { + RelativePoint point = new RelativePoint(e); + 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; + myCurrentOverContainer.startDropOver(myContent, point); + } + + if (myCurrentOverContainer != null) { + myCurrentOverContainer.processDropOver(myContent, point); + } + setLocationFrom(e); } else if (e.getID() == MouseEvent.MOUSE_RELEASED) { - createNewDockContainerFor(myContent, e); - stopCurrentDragSession(); + if (myCurrentOverContainer == null) { + createNewDockContainerFor(myContent, point); + stopCurrentDragSession(); + } else { + myCurrentOverContainer.add(myContent, point); + } } } @@ -186,24 +210,33 @@ public class DockManagerImpl extends DockManager { } } - private void createNewDockContainerFor(DockableContent content, MouseEvent event) { + @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 void createNewDockContainerFor(DockableContent content, RelativePoint point) { DockContainer container = content.getContainerFactory().createContainer(); register(container, null); DockWindow window = new DockWindow(myProject, container); window.show(); Dimension size = content.getPreferredSize(); - Point showPoint = event.getPoint(); - SwingUtilities.convertPointToScreen(showPoint, event.getComponent()); + Point showPoint = point.getPoint(); showPoint.x -= size.width / 2; - showPoint.y += size.width / 2; - - window.setLocation(showPoint); + showPoint.y -= size.width / 2; window.show(); + window.setLocation(showPoint); window.setSize(size); - container.add(content, new RelativePoint(event)); + + container.add(content, new RelativePoint(point.getComponent(), showPoint)); } private class DockWindow extends FrameWrapper { From 8d0c7a78b53ca89ab4bd1eaad4b058ff912751d1 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 19 Oct 2010 14:44:31 +0400 Subject: [PATCH 08/36] intermediate commit --- .../intellij/ui/docking/DockContainer.java | 4 +- .../com/intellij/ui/docking/DockManager.java | 2 +- .../src/com/intellij/ui/tabs/JBTabs.java | 2 + .../com/intellij/ui/tabs/impl/JBTabsImpl.java | 5 + .../impl/DockableEditorTabbedContainer.java | 22 ++++- .../impl/EditorTabbedContainer.java | 4 + .../openapi/fileEditor/impl/EditorWindow.java | 4 + .../fileEditor/impl/EditorsSplitters.java | 18 +++- .../impl/FileEditorManagerImpl.java | 2 +- .../com/intellij/openapi/ui/FrameWrapper.java | 17 ++-- .../ui/docking/impl/DockManagerImpl.java | 95 +++++++++++++++---- .../com/intellij/ui/awt/RelativePoint.java | 23 ++++- 12 files changed, 156 insertions(+), 42 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index 02ad0ca8ada4..53d650b2d454 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -21,7 +21,7 @@ import com.intellij.ui.awt.RelativeRectangle; import javax.swing.*; -public interface DockContainer { +public interface DockContainer extends Disposable { RelativeRectangle getAcceptArea(); boolean canAccept(DockableContent content, RelativePoint point); @@ -41,6 +41,8 @@ public interface DockContainer { void resetDropOver(DockableContent content); + boolean isDisposeWhenEmpty(); + interface Listener { void contentAdded(Object key); void contentRemoved(Object key); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java index 2033b4a0398f..66da6288a203 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -25,7 +25,7 @@ import java.util.Set; public abstract class DockManager implements ProjectComponent { - public abstract void register(DockContainer container, @Nullable Disposable parent); + public abstract void register(DockContainer container); public static DockManager getInstance(Project project) { return project.getComponent(DockManager.class); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java index 2e171b01fd53..9da1e11951a4 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java @@ -123,4 +123,6 @@ public interface JBTabs extends SwitchProvider { void resetDropOver(TabInfo tabInfo); void startDropOver(TabInfo tabInfo, RelativePoint point); void processDropOver(TabInfo over, RelativePoint point); + + boolean isEmptyVisible(); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index f4b66c5c232e..dba2039eba20 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -2933,4 +2933,9 @@ public class JBTabsImpl extends JComponent @Override public void processDropOver(TabInfo over, RelativePoint point) { } + + @Override + public boolean isEmptyVisible() { + return myVisibleInfos.isEmpty(); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index 42f6b6e1c1d6..e6728b3b55ad 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -43,19 +43,22 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain private JBTabs myCurrentOver; private TabInfo myCurrentOverInfo; + private boolean myDisposeWhenEmpty; + DockableEditorTabbedContainer(Project project) { - myProject = project; + this(project, null, true); } - DockableEditorTabbedContainer(Project project, EditorsSplitters splitters) { + DockableEditorTabbedContainer(Project project, EditorsSplitters splitters, boolean disposeWhenEmpty) { myProject = project; mySplitters = splitters; + myDisposeWhenEmpty = disposeWhenEmpty; } @Override public DockContainer createContainer() { if (mySplitters == null) { - mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject)) { + mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject), false) { @Override protected void afterFileClosed(VirtualFile file) { fireContentClosed(file); @@ -96,7 +99,7 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain @Override public void add(DockableContent content, RelativePoint dropTarget) { VirtualFile file = ((EditorTabbedContainer.MyDragOutDelegate.DockableEditor)content).getFile(); - EditorWindow currentWindow = mySplitters.getCurrentWindow(); + EditorWindow currentWindow = mySplitters.getOrCreateCurrentWindow(file); ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(currentWindow, file, true); } @@ -166,6 +169,15 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain @Override public boolean isEmpty() { - return mySplitters.getOpenFiles().length == 0; + return mySplitters.isEmptyVisible(); + } + + @Override + public void dispose() { + } + + @Override + public boolean isDisposeWhenEmpty() { + return myDisposeWhenEmpty; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index e2c5721b2a09..253c848c5598 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -281,6 +281,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; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index e77e66db92e2..9bb4c45da6be 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -307,6 +307,10 @@ public class EditorWindow { return myOwner; } + public boolean isEmptyVisible() { + return myTabbedPane != null ? myTabbedPane.isEmptyVisible() : getFiles().length == 0; + } + protected static class TComp extends JPanel implements DataProvider{ final EditorWithProviderComposite myEditor; diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index 930c707687ec..73b12fd9a1b0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -66,7 +66,7 @@ public class EditorsSplitters extends JPanel { private EditorWithProviderComposite myCurrentSelectedEditor; private final Alarm myIconUpdaterAlarm = new Alarm(); - public EditorsSplitters(final FileEditorManagerImpl manager) { + public EditorsSplitters(final FileEditorManagerImpl manager, boolean createOwnDockableContainer) { super(new BorderLayout()); setOpaque(true); setBackground(Color.GRAY); @@ -76,8 +76,10 @@ public class EditorsSplitters extends JPanel { setTransferHandler(new MyTransferHandler()); clear(); - DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), this); - DockManager.getInstance(myManager.getProject()).register(dockable, myManager.getProject()); + if (createOwnDockableContainer) { + DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), this, false); + DockManager.getInstance(myManager.getProject()).register(dockable); + } } public FileEditorManagerImpl getManager() { @@ -457,6 +459,16 @@ public class EditorsSplitters extends JPanel { 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) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index bf214b93ac49..b6a36ab956db 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -172,7 +172,7 @@ 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, true); myPanels.add(mySplitters, BorderLayout.CENTER); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java index fea4eec2abff..deb6fd93a510 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/FrameWrapper.java @@ -29,7 +29,6 @@ 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; @@ -57,7 +56,7 @@ public class FrameWrapper implements Disposable { private FocusWatcher myFocusWatcher; private ActionCallback myFocusedCallback; - private boolean isDisposed; + private boolean myDisposed; public FrameWrapper() { } @@ -120,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) { @@ -152,7 +155,7 @@ public class FrameWrapper implements Disposable { } public JFrame getFrame() { - assert !isDisposed : "Already disposed!"; + assert !myDisposed : "Already disposed!"; if (myFrame == null) { myFrame = createJFrame(); diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index d3bd5b8a6274..7dcbfc5802cc 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.ui.docking.impl; +import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.FrameWrapper; @@ -34,19 +35,20 @@ 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.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.*; public class DockManagerImpl extends DockManager { private Project myProject; private Set myContainers = new HashSet(); + private Map myWindows = new HashMap(); + private MyDragSession myCurrentDragSession; private BusyObject.Impl myBusyObject = new BusyObject.Impl() { @@ -60,10 +62,9 @@ public class DockManagerImpl extends DockManager { myProject = project; } - @Override - public void register(final DockContainer container, @Nullable Disposable parent) { + public void register(final DockContainer container) { myContainers.add(container); - Disposer.register(parent != null ? parent : myProject, new Disposable() { + Disposer.register(container, new Disposable() { @Override public void dispose() { myContainers.remove(container); @@ -79,6 +80,16 @@ public class DockManagerImpl extends DockManager { @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; } @@ -89,6 +100,15 @@ public class DockManagerImpl extends DockManager { myCurrentDragSession.cancel(); myCurrentDragSession = null; myBusyObject.onReady(); + + for (DockContainer each : myContainers) { + if (!each.isEmpty()) { + DockWindow window = myWindows.get(each); + if (window != null) { + window.setTransparrent(false); + } + } + } } } @@ -201,6 +221,7 @@ public class DockManagerImpl extends DockManager { stopCurrentDragSession(); } else { myCurrentOverContainer.add(myContent, point); + stopCurrentDragSession(); } } } @@ -214,7 +235,9 @@ public class DockManagerImpl extends DockManager { 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; + if (rec.contains(point) && each.canAccept(content, point)) { + return each; + } } return null; @@ -222,12 +245,13 @@ public class DockManagerImpl extends DockManager { private void createNewDockContainerFor(DockableContent content, RelativePoint point) { DockContainer container = content.getContainerFactory().createContainer(); - register(container, null); + register(container); DockWindow window = new DockWindow(myProject, container); + myWindows.put(container, window); window.show(); Dimension size = content.getPreferredSize(); - Point showPoint = point.getPoint(); + Point showPoint = point.getScreenPoint(); showPoint.x -= size.width / 2; showPoint.y -= size.width / 2; @@ -236,10 +260,10 @@ public class DockManagerImpl extends DockManager { window.setLocation(showPoint); window.setSize(size); - container.add(content, new RelativePoint(point.getComponent(), showPoint)); + container.add(content, new RelativePoint(showPoint)); } - private class DockWindow extends FrameWrapper { + private class DockWindow extends FrameWrapper implements IdeEventQueue.EventDispatcher { private DockContainer myContainer; @@ -247,24 +271,53 @@ public class DockManagerImpl extends DockManager { myContainer = container; setProject(project); setComponent(myContainer.getComponent()); + + IdeEventQueue.getInstance().addPostprocessor(this, this); + Disposer.register(myContainer, this); + myContainer.addListener(new DockContainer.Listener.Adapter() { @Override public void contentRemoved(Object key) { - if (myContainer.isEmpty()) { - WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true); - WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 1f); - - getReady().doWhenDone(new Runnable() { - @Override - public void run() { - close(); + getReady().doWhenDone(new Runnable() { + @Override + public void run() { + if (myContainer.isEmpty()) { + Disposer.dispose(myContainer); } - }); - } + } + }); } }, 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); + Disposer.dispose(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(); diff --git a/platform/util/src/com/intellij/ui/awt/RelativePoint.java b/platform/util/src/com/intellij/ui/awt/RelativePoint.java index 90598966ed38..4c36294730f4 100644 --- a/platform/util/src/com/intellij/ui/awt/RelativePoint.java +++ b/platform/util/src/com/intellij/ui/awt/RelativePoint.java @@ -21,14 +21,25 @@ import java.awt.event.MouseEvent; public class RelativePoint { - private final Component myComponent; - private final Point myPointOnComponent; + private Component myComponent; + private Point myPointOnComponent; public RelativePoint(MouseEvent event) { - this(event.getComponent(), event.getPoint()); + init(event.getComponent(), event.getPoint()); } public RelativePoint(Component aComponent, Point aPointOnComponent) { + init(aComponent, aPointOnComponent); + } + + public RelativePoint(Point screenPoint) { + Point p = new Point(screenPoint.x, screenPoint.y); + Frame c = JOptionPane.getRootFrame(); + SwingUtilities.convertPointFromScreen(p, c); + init(c, p); + } + + private void init(Component aComponent, Point aPointOnComponent) { if (aComponent.isShowing()) { myComponent = SwingUtilities.getRootPane(aComponent); myPointOnComponent = SwingUtilities.convertPoint(aComponent, aPointOnComponent, myComponent); @@ -103,4 +114,10 @@ public class RelativePoint { final Point point = new Point(visibleRect.x + visibleRect.width, visibleRect.y); return new RelativePoint(component, point); } + + public static RelativePoint fromScreen(Point screenPoint) { + Frame root = JOptionPane.getRootFrame(); + SwingUtilities.convertPointFromScreen(screenPoint, root); + return new RelativePoint(root, screenPoint); + } } From 799a24192c237eea032d662725a76e40c2142cac Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Wed, 20 Oct 2010 15:57:23 +0400 Subject: [PATCH 09/36] editor detach: dnd between splits --- .../impl/DockableEditorTabbedContainer.java | 12 ++++++++++-- .../fileEditor/impl/EditorTabbedContainer.java | 4 ++++ .../intellij/ui/docking/impl/DockManagerImpl.java | 5 +++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index e6728b3b55ad..c69b0a03970c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -98,9 +98,17 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain @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(); - EditorWindow currentWindow = mySplitters.getOrCreateCurrentWindow(file); - ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(currentWindow, file, true); + + if (window == null) { + window = mySplitters.getOrCreateCurrentWindow(file); + } + ((FileEditorManagerImpl)FileEditorManagerEx.getInstanceEx(myProject)).openFileImpl2(window, file, true); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 253c848c5598..fda731e6b41e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -390,6 +390,10 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } } + if (EditorWindow.DATA_KEY.is(dataId)) { + return myWindow; + } + return null; } } diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 7dcbfc5802cc..4fc41a7530c9 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -228,6 +228,11 @@ public class DockManagerImpl extends DockManager { public void cancel() { myWindow.dispose(); + + if (myCurrentOverContainer != null) { + myCurrentOverContainer.resetDropOver(myContent); + myCurrentOverContainer = null; + } } } From 8520d527b15d6f32ef2fb21703bb074ccb346785 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Fri, 22 Oct 2010 21:20:09 +0400 Subject: [PATCH 10/36] editor detach: insert at index + highlights --- .../intellij/ui/docking/DockContainer.java | 11 +- .../src/com/intellij/ui/tabs/JBTabs.java | 2 +- .../com/intellij/ui/tabs/impl/JBTabsImpl.java | 106 +++++++++++++++--- .../intellij/ui/tabs/impl/LayoutPassInfo.java | 7 ++ .../com/intellij/ui/tabs/impl/TabLabel.java | 27 +++-- .../com/intellij/ui/tabs/impl/TabLayout.java | 2 + .../tabs/impl/singleRow/SingleRowLayout.java | 42 ++++--- .../impl/singleRow/SingleRowPassInfo.java | 12 +- .../ui/tabs/impl/table/TableLayout.java | 18 +-- .../ui/tabs/impl/table/TablePassInfo.java | 4 +- .../impl/DockableEditorTabbedContainer.java | 35 +++++- .../impl/EditorTabbedContainer.java | 10 +- .../ui/docking/impl/DockManagerImpl.java | 34 ++++-- 13 files changed, 237 insertions(+), 73 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index 53d650b2d454..84327c066b99 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -18,8 +18,10 @@ package com.intellij.ui.docking; import com.intellij.openapi.Disposable; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.awt.RelativeRectangle; +import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; public interface DockContainer extends Disposable { @@ -36,8 +38,13 @@ public interface DockContainer extends Disposable { boolean isEmpty(); - void startDropOver(DockableContent content, RelativePoint point); - void processDropOver(DockableContent content, RelativePoint point); + @Nullable + Image startDropOver(DockableContent content, RelativePoint point); + + @Nullable + Image processDropOver(DockableContent content, RelativePoint point); + + @Nullable void resetDropOver(DockableContent content); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java index 9da1e11951a4..cc602f5c00f0 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java @@ -121,7 +121,7 @@ public interface JBTabs extends SwitchProvider { Image getComponentImage(TabInfo info); void resetDropOver(TabInfo tabInfo); - void startDropOver(TabInfo tabInfo, RelativePoint point); + Image startDropOver(TabInfo tabInfo, RelativePoint point); void processDropOver(TabInfo over, RelativePoint point); boolean isEmptyVisible(); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index dba2039eba20..0d2dbbdb041c 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -67,7 +67,7 @@ public class JBTabsImpl extends JComponent static DataKey NAVIGATION_ACTIONS_KEY = DataKey.create("JBTabs"); ActionManager myActionManager; - public final List myVisibleInfos = new ArrayList(); + private final List myVisibleInfos = new ArrayList(); private final Map myHiddenInfos = new HashMap(); private TabInfo mySelectedInfo; @@ -160,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); @@ -410,6 +412,10 @@ public class JBTabsImpl extends JComponent } } + public boolean isDropTarget(TabInfo info) { + return myDropInfo != null && myDropInfo == info; + } + class TabActionsAutoHideListener extends MouseMotionAdapter { private TabLabel myCurrentOverLabel; @@ -571,7 +577,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)); } @@ -579,14 +589,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(); @@ -607,9 +619,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; @@ -1211,12 +1225,23 @@ public class JBTabsImpl extends JComponent } + List visible = new ArrayList(); + visible.addAll(myVisibleInfos); + + if (myDropInfo != null && !visible.contains(myDropInfo)) { + if (myDropInfoIndex >= 0 && myDropInfoIndex < visible.size()) { + visible.add(myDropInfoIndex, 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; } @@ -1335,6 +1360,10 @@ public class JBTabsImpl extends JComponent reset(each, resetLabels); } + if (myDropInfo != null) { + reset(myDropInfo, resetLabels); + } + for (TabInfo each : myHiddenInfos.keySet()) { reset(each, resetLabels); } @@ -1713,6 +1742,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; @@ -2114,7 +2145,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(); @@ -2924,14 +2965,45 @@ public class JBTabsImpl extends JComponent @Override public void resetDropOver(TabInfo tabInfo) { + if (myDropInfo != null) { + TabInfo dropInfo = myDropInfo; + myDropInfo = null; + removeTab(dropInfo, null, false, true); + } } @Override - public void startDropOver(TabInfo tabInfo, RelativePoint point) { + public Image startDropOver(TabInfo tabInfo, RelativePoint point) { + myDropInfo = tabInfo; + + addTab(myDropInfo, -1, true); + myDropInfoIndex = myLayout.getDropIndexFor(point.getPoint(this)); + + 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 != myDropInfoIndex) { + myDropInfoIndex = index; + relayout(true, false); + } + } + + public int getDropInfoIndex() { + return myDropInfoIndex; } @Override diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java index c8e2bc301e26..904a78f463d3 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/LayoutPassInfo.java @@ -21,6 +21,13 @@ import java.awt.*; import java.util.List; public abstract class LayoutPassInfo { + + public java.util.List myVisibleInfos; + + protected LayoutPassInfo(List visibleInfos) { + myVisibleInfos = visibleInfos; + } + public abstract TabInfo getPreviousFor(TabInfo info); public abstract TabInfo getNextFor(TabInfo info); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java index d027ace3397b..745a84d233e8 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java @@ -110,7 +110,14 @@ public class TabLabel extends JPanel { myCentered = toCenter; } + public void paintOffscreen(Graphics g) { + validateTree(); + doPaint(g); + } + public void paint(final Graphics g) { + if (myTabs.isDropTarget(myInfo)) return; + if (myTabs.getSelectedInfo() != myInfo) { myImage = null; doPaint(g); @@ -167,18 +174,22 @@ public class TabLabel extends JPanel { break; } - if (myTabs.getSelectedInfo() != myInfo) { - g.translate(dX, dY); - } else { - g.translate(dXs, dYs); + if (!myTabs.isDropTarget(myInfo)) { + if (myTabs.getSelectedInfo() != myInfo) { + g.translate(dX, dY); + } else { + g.translate(dXs, dYs); + } } super.paint(g); - if (myTabs.getSelectedInfo() != myInfo) { - g.translate(-dX, -dY); - } else { - g.translate(-dXs, -dYs); + if (!myTabs.isDropTarget(myInfo)) { + if (myTabs.getSelectedInfo() != myInfo) { + g.translate(-dX, -dY); + } else { + g.translate(-dXs, -dYs); + } } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java index 2cee099bc399..8a452c998e70 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLayout.java @@ -16,6 +16,7 @@ package com.intellij.ui.tabs.impl; import com.intellij.openapi.util.registry.Registry; +import com.intellij.ui.awt.RelativePoint; import java.awt.*; @@ -42,4 +43,5 @@ public abstract class TabLayout { return Registry.doubleValue("ide.tabbedPane.dragOutMultiplier"); } + public abstract int getDropIndexFor(Point point); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java index 1b62b9792654..40a67d61b184 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java @@ -17,7 +17,6 @@ package com.intellij.ui.tabs.impl.singleRow; import com.intellij.ui.tabs.TabInfo; import com.intellij.ui.tabs.impl.*; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -94,14 +93,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; @@ -117,12 +116,12 @@ public class SingleRowLayout extends TabLayout { return layoutLabels; } - public LayoutPassInfo layoutSingleRow() { - SingleRowPassInfo data = new SingleRowPassInfo(this); + public LayoutPassInfo layoutSingleRow(java.util.List visibleInfos) { + SingleRowPassInfo data = new SingleRowPassInfo(this, visibleInfos); final TabInfo selected = myTabs.getSelectedInfo(); final JBTabsImpl.Toolbar selectedToolbar = myTabs.myInfo2Toolbar.get(selected); - final boolean layoutLabels = checkLayoutLabels(); + final boolean layoutLabels = checkLayoutLabels(data); if (!layoutLabels) { data = myLastSingRowLayout; } @@ -153,10 +152,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); @@ -241,7 +240,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); } @@ -278,10 +277,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); @@ -345,4 +344,17 @@ public class SingleRowLayout extends TabLayout { } } } + + @Override + public int getDropIndexFor(Point point) { + if (myLastSingRowLayout == null) return -1; + + Component c = myTabs.getComponentAt(point); + if (c instanceof TabLabel) { + TabInfo info = ((TabLabel)c).getInfo(); + return myLastSingRowLayout.myVisibleInfos.indexOf(info); + } + + return -1; + } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java index 87b3dd61e9de..bbc9057dccb6 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowPassInfo.java @@ -51,7 +51,9 @@ public class SingleRowPassInfo extends LayoutPassInfo { public JComponent comp; public Rectangle tabRectangle; - public SingleRowPassInfo(SingleRowLayout layout) { + + public SingleRowPassInfo(SingleRowLayout layout, java.util.List visibleInfos) { + super(visibleInfos); myTabs = layout.myTabs; laayoutSize = layout.myTabs.getSize(); contentCount = myTabs.getTabCount(); @@ -61,11 +63,11 @@ public class SingleRowPassInfo extends LayoutPassInfo { } public TabInfo getPreviousFor(final TabInfo info) { - return getPrevious(myTabs.myVisibleInfos, myTabs.myVisibleInfos.indexOf(info)); + return getPrevious(myVisibleInfos, myVisibleInfos.indexOf(info)); } public TabInfo getNextFor(final TabInfo info) { - return getNext(myTabs.myVisibleInfos, myTabs.myVisibleInfos.indexOf(info)); + return getNext(myVisibleInfos, myVisibleInfos.indexOf(info)); } public int getRowCount() { @@ -73,11 +75,11 @@ public class SingleRowPassInfo extends LayoutPassInfo { } public int getColumnCount(final int row) { - return myTabs.myVisibleInfos.size(); + return myVisibleInfos.size(); } public TabInfo getTabAt(final int row, final int column) { - return myTabs.myVisibleInfos.get(column); + return myVisibleInfos.get(column); } public Rectangle getHeaderRectangle() { diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java index 2efd1ec0d6b3..0602d22eeb42 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TableLayout.java @@ -34,8 +34,8 @@ public class TableLayout extends TabLayout { myTabs = tabs; } - private TablePassInfo computeLayoutTable() { - final TablePassInfo data = new TablePassInfo(myTabs); + private TablePassInfo computeLayoutTable(java.util.List visibleInfos) { + final TablePassInfo data = new TablePassInfo(myTabs, visibleInfos); final Insets insets = myTabs.getLayoutInsets(); data.toFitRec = @@ -46,7 +46,7 @@ public class TableLayout extends TabLayout { data.requiredRows = 1; - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(eachInfo); final Dimension size = eachLabel.getPreferredSize(); if (eachX + size.width >= data.toFitRec.getMaxX()) { @@ -65,7 +65,7 @@ public class TableLayout extends TabLayout { if (data.requiredRows > 1) { final int rowFit = insets.left + data.requiredWidth / data.requiredRows; - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(eachInfo); final Rectangle eachBounds = eachLabel.getBounds(); if (eachBounds.contains(rowFit, 0)) { @@ -75,7 +75,7 @@ public class TableLayout extends TabLayout { } } - for (TabInfo eachInfo : myTabs.myVisibleInfos) { + for (TabInfo eachInfo : data.myVisibleInfos) { final TabLabel eachLabel = myTabs.myInfo2Label.get(eachInfo); final Dimension size = eachLabel.getPreferredSize(); if (eachX + size.width <= data.rowToFitMaxX) { @@ -112,9 +112,9 @@ public class TableLayout extends TabLayout { } - public LayoutPassInfo layoutTable() { + public LayoutPassInfo layoutTable(java.util.List visibleInfos) { myTabs.resetLayout(true); - final TablePassInfo data = computeLayoutTable(); + final TablePassInfo data = computeLayoutTable(visibleInfos); final Insets insets = myTabs.getLayoutInsets(); int eachY = insets.top; int eachX; @@ -169,4 +169,8 @@ public class TableLayout extends TabLayout { return data; } + @Override + public int getDropIndexFor(Point point) { + return -1; + } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java index 176660a0daa1..13073a83f7e9 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java @@ -36,8 +36,8 @@ public class TablePassInfo extends LayoutPassInfo { JBTabsImpl myTabs; - TablePassInfo(final JBTabsImpl tabs) { - myTabs = tabs; + TablePassInfo(final JBTabsImpl tabs, java.util.List visibleInfos) { + super(visibleInfos); } public TabInfo getPreviousFor(final TabInfo info) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index c69b0a03970c..d2f805274474 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -29,8 +29,10 @@ import com.intellij.ui.docking.DockContainerFactory; 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 javax.swing.*; +import java.awt.*; import java.util.concurrent.CopyOnWriteArraySet; class DockableEditorTabbedContainer implements DockContainerFactory, DockContainer { @@ -41,6 +43,7 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain private CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); private JBTabs myCurrentOver; + private Image myCurrentOverImg; private TabInfo myCurrentOverInfo; private boolean myDisposeWhenEmpty; @@ -93,7 +96,17 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain @Override public boolean canAccept(DockableContent content, RelativePoint point) { - return content instanceof EditorTabbedContainer.MyDragOutDelegate.DockableEditor; + 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 @@ -105,18 +118,27 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain 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 void startDropOver(DockableContent content, RelativePoint point) { + public Image startDropOver(DockableContent content, RelativePoint point) { + return null; } @Override - public void processDropOver(DockableContent content, RelativePoint point) { + public Image processDropOver(DockableContent content, RelativePoint point) { JBTabs current = mySplitters.getTabsAt(point); if (myCurrentOver != null && myCurrentOver != current) { resetDropOver(content); @@ -125,13 +147,15 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain if (myCurrentOver == null && current != null) { myCurrentOver = current; Presentation presentation = content.getPresentation(); - myCurrentOverInfo = new TabInfo(null).setText(presentation.getText()).setIcon(presentation.getIcon()); - myCurrentOver.startDropOver(myCurrentOverInfo, point); + 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 @@ -140,6 +164,7 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain myCurrentOver.resetDropOver(myCurrentOverInfo); myCurrentOver = null; myCurrentOverInfo = null; + myCurrentOverImg = null; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index fda731e6b41e..2ae781928469 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -506,7 +506,7 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget myFile = (VirtualFile)info.getObject(); Presentation presentation = new Presentation(info.getText()); presentation.setIcon(info.getIcon()); - mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile, presentation)); + mySession = getDockManager().createDragSession(mouseEvent, new DockableEditor(img, myFile, presentation, myWindow)); } private DockManager getDockManager() { @@ -532,13 +532,15 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget final Image myImg; private DockableEditorTabbedContainer myContainer; private Presentation myPresentation; + private EditorWindow myEditorWindow; - public DockableEditor(Image img, VirtualFile file, Presentation presentation) { + public DockableEditor(Image img, VirtualFile file, Presentation presentation, EditorWindow window) { myImg = img; myFile = file; myPresentation = presentation; myContainer = new DockableEditorTabbedContainer(myProject); + myEditorWindow = window; } @Override @@ -561,6 +563,10 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget return myContainer; } + public EditorWindow getEditorWindow() { + return myEditorWindow; + } + @Override public Presentation getPresentation() { return myPresentation; diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 4fc41a7530c9..442f57123c90 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -141,10 +141,14 @@ public class DockManagerImpl extends DockManager { private class MyDragSession implements DragSession { private JWindow myWindow; - private Image myThumbnail; + + private Image myDragImage; + private Image myDefaultDragImage; + private DockableContent myContent; private DockContainer myCurrentOverContainer; + private JLabel myImageContainer; private MyDragSession(MouseEvent me, DockableContent content) { myWindow = new JWindow(); @@ -168,12 +172,13 @@ public class DockManagerImpl extends DockManager { BufferedImage buffer = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB); buffer.createGraphics().drawImage(previewImage, 0, 0, (int)width, (int)height, null); - myThumbnail = buffer.getScaledInstance((int)(width * ratio), (int)(height * ratio), Image.SCALE_SMOOTH); + myDefaultDragImage = buffer.getScaledInstance((int)(width * ratio), (int)(height * ratio), Image.SCALE_SMOOTH); + myDragImage = myDefaultDragImage; myWindow.getContentPane().setLayout(new BorderLayout()); - JLabel label = new JLabel(new ImageIcon(myThumbnail)); - label.setBorder(new LineBorder(Color.lightGray)); - myWindow.getContentPane().add(label, BorderLayout.CENTER); + myImageContainer = new JLabel(new ImageIcon(myDragImage)); + myImageContainer.setBorder(new LineBorder(Color.lightGray)); + myWindow.getContentPane().add(myImageContainer, BorderLayout.CENTER); setLocationFrom(me); @@ -188,15 +193,16 @@ public class DockManagerImpl extends DockManager { Point showPoint = me.getPoint(); SwingUtilities.convertPointToScreen(showPoint, me.getComponent()); - showPoint.x -= myThumbnail.getWidth(null) / 2; + showPoint.x -= myDragImage.getWidth(null) / 2; showPoint.y += 10; - myWindow.setBounds(new Rectangle(showPoint, new Dimension(myThumbnail.getWidth(null), myThumbnail.getHeight(null)))); + 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) { @@ -206,11 +212,21 @@ public class DockManagerImpl extends DockManager { if (myCurrentOverContainer == null && over != null) { myCurrentOverContainer = over; - myCurrentOverContainer.startDropOver(myContent, point); + img = myCurrentOverContainer.startDropOver(myContent, point); } if (myCurrentOverContainer != null) { - myCurrentOverContainer.processDropOver(myContent, point); + img = myCurrentOverContainer.processDropOver(myContent, point); + } + + if (img == null) { + img = myDefaultDragImage; + } + + if (img != myDragImage) { + myDragImage = img; + myImageContainer.setIcon(new ImageIcon(myDragImage)); + myWindow.pack(); } setLocationFrom(e); From 610f6fd889a8a56c4aae40109ace5b6107053935 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 25 Oct 2010 14:50:20 +0400 Subject: [PATCH 11/36] editor detach: dropping to a single-row ghost component --- .../ui/tabs/impl/singleRow/SingleRowLayout.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java index 40a67d61b184..f89320dc44a4 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java @@ -15,6 +15,7 @@ */ 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.*; @@ -353,6 +354,17 @@ public class SingleRowLayout extends TabLayout { if (c instanceof TabLabel) { TabInfo info = ((TabLabel)c).getInfo(); return myLastSingRowLayout.myVisibleInfos.indexOf(info); + } 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; + return index >= 0 && index < myLastSingRowLayout.myVisibleInfos.size() ? index : -1; + } else { + if (myLastSingRowLayout.myVisibleInfos.size() == 0) return 0; + return myLeftGhost == ghost ? 0 : myLastSingRowLayout.myVisibleInfos.size() - 1; + } } return -1; From ef5227cc52775aefffa61d0cf6eea54247a87b50 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Mon, 25 Oct 2010 19:01:50 +0400 Subject: [PATCH 12/36] editor detach: fixed jumping bug --- .../com/intellij/ui/tabs/impl/JBTabsImpl.java | 18 ++++-- .../tabs/impl/singleRow/SingleRowLayout.java | 60 +++++++++++++++++-- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 0d2dbbdb041c..8e1a78aeb309 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -416,6 +416,10 @@ public class JBTabsImpl extends JComponent return myDropInfo != null && myDropInfo == info; } + public void setDropInfoIndex(int dropInfoIndex) { + myDropInfoIndex = dropInfoIndex; + } + class TabActionsAutoHideListener extends MouseMotionAdapter { private TabLabel myCurrentOverLabel; @@ -1229,8 +1233,8 @@ public class JBTabsImpl extends JComponent visible.addAll(myVisibleInfos); if (myDropInfo != null && !visible.contains(myDropInfo)) { - if (myDropInfoIndex >= 0 && myDropInfoIndex < visible.size()) { - visible.add(myDropInfoIndex, myDropInfo); + if (getDropInfoIndex() >= 0 && getDropInfoIndex() < visible.size()) { + visible.add(getDropInfoIndex(), myDropInfo); } else { visible.add(myDropInfo); } @@ -2968,6 +2972,7 @@ public class JBTabsImpl extends JComponent if (myDropInfo != null) { TabInfo dropInfo = myDropInfo; myDropInfo = null; + setDropInfoIndex(-1); removeTab(dropInfo, null, false, true); } } @@ -2976,8 +2981,9 @@ public class JBTabsImpl extends JComponent public Image startDropOver(TabInfo tabInfo, RelativePoint point) { myDropInfo = tabInfo; - addTab(myDropInfo, -1, true); - myDropInfoIndex = myLayout.getDropIndexFor(point.getPoint(this)); + int index = myLayout.getDropIndexFor(point.getPoint(this)); + setDropInfoIndex(index); + addTab(myDropInfo, index, true); TabLabel label = myInfo2Label.get(myDropInfo); Dimension size = label.getPreferredSize(); @@ -2996,8 +3002,8 @@ public class JBTabsImpl extends JComponent @Override public void processDropOver(TabInfo over, RelativePoint point) { int index = myLayout.getDropIndexFor(point.getPoint(this)); - if (index != myDropInfoIndex) { - myDropInfoIndex = index; + if (index != getDropInfoIndex()) { + setDropInfoIndex(index); relayout(true, false); } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java index f89320dc44a4..f670c95ebce3 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java @@ -350,23 +350,73 @@ public class SingleRowLayout extends TabLayout { 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(); - return myLastSingRowLayout.myVisibleInfos.indexOf(info); + 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; - return index >= 0 && index < myLastSingRowLayout.myVisibleInfos.size() ? index : -1; + result = index >= 0 && index < myLastSingRowLayout.myVisibleInfos.size() ? index : -1; } else { - if (myLastSingRowLayout.myVisibleInfos.size() == 0) return 0; - return myLeftGhost == ghost ? 0 : myLastSingRowLayout.myVisibleInfos.size() - 1; + if (myLastSingRowLayout.myVisibleInfos.size() == 0) { + result = 0; + } else { + result = myLeftGhost == ghost ? 0 : myLastSingRowLayout.myVisibleInfos.size() - 1; + } } } - return -1; + System.out.println(result + " " + myLastSingRowLayout.myVisibleInfos + " c=" + c); + return result; } } From acda5a1806674f2f3636b1b4ff0615a58189aeac Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 26 Oct 2010 16:13:15 +0400 Subject: [PATCH 13/36] editor detach: interm commit --- .../intellij/ui/docking/DockContainer.java | 9 ++++ .../com/intellij/ui/docking/DockManager.java | 3 ++ .../tabs/impl/singleRow/SingleRowLayout.java | 1 - .../impl/DockableEditorTabbedContainer.java | 15 +++++- .../ui/docking/impl/DockManagerImpl.java | 46 +++++++++++++++++-- 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index 84327c066b99..ab8a23491f7c 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -16,8 +16,10 @@ 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 org.jdom.Element; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -50,6 +52,13 @@ public interface DockContainer extends Disposable { boolean isDisposeWhenEmpty(); + interface Persistent extends DockContainer { + + Element getState(); + String getLoadStateMethod(); + + } + interface Listener { void contentAdded(Object key); void contentRemoved(Object key); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java index 66da6288a203..9832161c052a 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -16,7 +16,10 @@ 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; diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java index f670c95ebce3..8255e217f2cd 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/singleRow/SingleRowLayout.java @@ -416,7 +416,6 @@ public class SingleRowLayout extends TabLayout { } } - System.out.println(result + " " + myLastSingRowLayout.myVisibleInfos + " c=" + c); return result; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index d2f805274474..224ff556c55e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -30,12 +30,13 @@ 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 DockContainerFactory, DockContainer { +class DockableEditorTabbedContainer implements DockContainerFactory, DockContainer.Persistent { private EditorsSplitters mySplitters; private Project myProject; @@ -58,6 +59,18 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain myDisposeWhenEmpty = disposeWhenEmpty; } + @Override + public Element getState() { + Element editors = new Element("editors"); + mySplitters.writeExternal(editors); + return editors; + } + + @Override + public String getLoadStateMethod() { + return getClass().getCanonicalName() + "." + "loadState"; + } + @Override public DockContainer createContainer() { if (mySplitters == null) { diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 442f57123c90..fe9df7b53270 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -17,6 +17,9 @@ 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; @@ -29,6 +32,7 @@ import com.intellij.ui.docking.DockContainer; import com.intellij.ui.docking.DockManager; import com.intellij.ui.docking.DockableContent; import com.intellij.ui.docking.DragSession; +import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,7 +46,13 @@ import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.util.*; -public class DockManagerImpl extends DockManager { +@State( + name = "DockManager", + storages = {@Storage( + id = "other", + file = "$WORKSPACE_FILE$")}) + +public class DockManagerImpl extends DockManager implements PersistentStateComponent{ private Project myProject; @@ -58,6 +68,8 @@ public class DockManagerImpl extends DockManager { } }; + private int myWindowIdCounter = 1; + public DockManagerImpl(Project project) { myProject = project; } @@ -267,7 +279,7 @@ public class DockManagerImpl extends DockManager { private void createNewDockContainerFor(DockableContent content, RelativePoint point) { DockContainer container = content.getContainerFactory().createContainer(); register(container); - DockWindow window = new DockWindow(myProject, container); + DockWindow window = new DockWindow(String.valueOf(myWindowIdCounter++), myProject, container); myWindows.put(container, window); window.show(); @@ -286,9 +298,11 @@ public class DockManagerImpl extends DockManager { private class DockWindow extends FrameWrapper implements IdeEventQueue.EventDispatcher { + private String myId; private DockContainer myContainer; - private DockWindow(Project project, DockContainer container) { + private DockWindow(String id, Project project, DockContainer container) { + myId = id; myContainer = container; setProject(project); setComponent(myContainer.getComponent()); @@ -351,4 +365,30 @@ public class DockManagerImpl extends DockManager { 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("loadMethod", eachContainer.getLoadStateMethod()); + content.addContent(eachContainer.getState()); + eachWindowElement.addContent(content); + + root.addContent(eachWindowElement); + } + } + } + return root; + } + + @Override + public void loadState(Element state) { + } } From b3d4b3445bf3f230dc71c430fcd97cb63cdef95f Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Wed, 27 Oct 2010 18:45:40 +0400 Subject: [PATCH 14/36] editor detach: interm commit - 2 --- .../impl/PsiAwareFileEditorManagerImpl.java | 5 +- .../intellij/ui/docking/DockContainer.java | 2 +- .../ui/docking/DockContainerFactory.java | 11 +++- .../com/intellij/ui/docking/DockManager.java | 1 + .../intellij/ui/docking/DockableContent.java | 2 +- .../impl/DockableEditorContainerFactory.java | 61 +++++++++++++++++++ .../impl/DockableEditorTabbedContainer.java | 45 +++++--------- .../impl/EditorTabbedContainer.java | 10 +-- .../openapi/fileEditor/impl/EditorWindow.java | 2 +- .../fileEditor/impl/EditorsSplitters.java | 8 ++- .../impl/FileEditorManagerImpl.java | 18 +++++- .../ui/docking/impl/DockManagerImpl.java | 40 ++++++++---- 12 files changed, 148 insertions(+), 57 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java index 62ba53c03c45..232db7d9ee4f 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java @@ -28,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.problems.WolfTheProblemSolver; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.ui.docking.DockManager; import org.jetbrains.annotations.NotNull; import java.awt.*; @@ -47,8 +48,8 @@ public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { private final MyPsiTreeChangeListener myPsiTreeChangeListener; private final WolfTheProblemSolver.ProblemListener myProblemListener; - public PsiAwareFileEditorManagerImpl(final Project project, final PsiManager psiManager, final WolfTheProblemSolver problemSolver) { - super(project); + public PsiAwareFileEditorManagerImpl(final Project project, final PsiManager psiManager, final WolfTheProblemSolver problemSolver, DockManager dockManager) { + super(project, dockManager); myPsiManager = psiManager; myProblemSolver = problemSolver; myPsiTreeChangeListener = new MyPsiTreeChangeListener(); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index ab8a23491f7c..56b6809224b7 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -54,8 +54,8 @@ public interface DockContainer extends Disposable { interface Persistent extends DockContainer { + String getDockContainerType(); Element getState(); - String getLoadStateMethod(); } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java index a7ed507e6f34..f802af0b23e7 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java @@ -15,8 +15,17 @@ */ package com.intellij.ui.docking; -public interface DockContainerFactory { +import com.intellij.openapi.Disposable; +import org.jdom.Element; + +public interface DockContainerFactory extends Disposable { DockContainer createContainer(); + interface Persistent extends DockContainerFactory { + + void loadState(Element element); + + } + } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java index 9832161c052a..2aac3d3c71db 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockManager.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockManager.java @@ -29,6 +29,7 @@ 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); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java index f82f799f1676..e4981fb47eb4 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockableContent.java @@ -25,7 +25,7 @@ public interface DockableContent { Image getPreviewImage(); - DockContainerFactory getContainerFactory(); + String getDockContainerType(); Dimension getPreferredSize(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java new file mode 100644 index 000000000000..e70e83054244 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java @@ -0,0 +1,61 @@ +/* + * 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; + +public class DockableEditorContainerFactory implements DockContainerFactory { + + public static final String TYPE = "file-editors"; + + private Project myProject; + private FileEditorManagerImpl myFileEditorManager; + private DockManager myDockManager; + + public DockableEditorContainerFactory(Project project, FileEditorManagerImpl fileEditorManager, DockManager dockManager) { + this.myProject = project; + myFileEditorManager = fileEditorManager; + myDockManager = dockManager; + } + + @Override + public DockContainer createContainer() { + final Ref container = new Ref(); + EditorsSplitters splitters = new EditorsSplitters(myFileEditorManager, myDockManager, false) { + @Override + protected void afterFileClosed(VirtualFile file) { + container.get().fireContentClosed(file); + } + + @Override + protected void afterFileOpen(VirtualFile file) { + container.get().fireContentOpen(file); + } + }; + splitters.createCurrentWindow(); + container.set(new DockableEditorTabbedContainer(myProject, myDockManager, splitters, true)); + return container.get(); + } + + @Override + public void dispose() { + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index 224ff556c55e..9663a43e12fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -26,6 +26,7 @@ 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; @@ -36,7 +37,7 @@ import javax.swing.*; import java.awt.*; import java.util.concurrent.CopyOnWriteArraySet; -class DockableEditorTabbedContainer implements DockContainerFactory, DockContainer.Persistent { +class DockableEditorTabbedContainer implements DockContainer.Persistent { private EditorsSplitters mySplitters; private Project myProject; @@ -48,17 +49,24 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain private TabInfo myCurrentOverInfo; private boolean myDisposeWhenEmpty; + private DockManager myDockManager; - DockableEditorTabbedContainer(Project project) { - this(project, null, true); + DockableEditorTabbedContainer(Project project, DockManager dockManager) { + this(project, dockManager, null, true); } - DockableEditorTabbedContainer(Project project, EditorsSplitters splitters, boolean disposeWhenEmpty) { + 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("editors"); @@ -66,37 +74,13 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain return editors; } - @Override - public String getLoadStateMethod() { - return getClass().getCanonicalName() + "." + "loadState"; - } - - @Override - public DockContainer createContainer() { - if (mySplitters == null) { - mySplitters = new EditorsSplitters((FileEditorManagerImpl)FileEditorManager.getInstance(myProject), false) { - @Override - protected void afterFileClosed(VirtualFile file) { - fireContentClosed(file); - } - - @Override - protected void afterFileOpen(VirtualFile file) { - fireContentOpen(file); - } - }; - mySplitters.createCurrentWindow(); - } - return this; - } - - private void fireContentClosed(VirtualFile file) { + void fireContentClosed(VirtualFile file) { for (Listener each : myListeners) { each.contentRemoved(file); } } - private void fireContentOpen(VirtualFile file) { + void fireContentOpen(VirtualFile file) { for (Listener each : myListeners) { each.contentAdded(file); } @@ -220,6 +204,7 @@ class DockableEditorTabbedContainer implements DockContainerFactory, DockContain @Override public void dispose() { + closeAll(); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 2ae781928469..dd81146ddddf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -70,10 +70,12 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget @NonNls public static final String HELP_ID = "ideaInterface.editor"; private TabInfo.DragOutDelegate myDragOutDelegate = new MyDragOutDelegate(); + private DockManager myDockManager; - EditorTabbedContainer(final EditorWindow window, Project project, int tabPlacement) { + 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() { @@ -539,7 +541,7 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget myImg = img; myFile = file; myPresentation = presentation; - myContainer = new DockableEditorTabbedContainer(myProject); + myContainer = new DockableEditorTabbedContainer(myProject, myDockManager); myEditorWindow = window; } @@ -559,8 +561,8 @@ final class EditorTabbedContainer implements Disposable, CloseAction.CloseTarget } @Override - public DockContainerFactory getContainerFactory() { - return myContainer; + public String getDockContainerType() { + return DockableEditorContainerFactory.TYPE; } public EditorWindow getEditorWindow() { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 9bb4c45da6be..e3322494a183 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -87,7 +87,7 @@ public class EditorWindow { private void createTabs(int tabPlacement) { LOG.assertTrue (myTabbedPane == null); - myTabbedPane = new EditorTabbedContainer(this, getManager().getProject(), tabPlacement); + myTabbedPane = new EditorTabbedContainer(this, getManager().getProject(), getManager().getDockManager(), tabPlacement); myPanel.add(myTabbedPane.getComponent(), BorderLayout.CENTER); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index 73b12fd9a1b0..ecefc2e1228b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -65,20 +65,22 @@ public 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, boolean createOwnDockableContainer) { + 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(), this, false); - DockManager.getInstance(myManager.getProject()).register(dockable); + DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), dockManager, this, false); + myDockManager.register(dockable); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index b6a36ab956db..5335157e4364 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -103,11 +103,17 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec * Removes invalid myEditor and updates "modified" status. */ private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener(); + private DockManager myDockManager; - public FileEditorManagerImpl(final Project project) { + public FileEditorManagerImpl(final Project project, DockManager dockManager) { /* ApplicationManager.getApplication().assertIsDispatchThread(); */ myProject = project; + myDockManager = dockManager; myListenerList = new MessageListenerList(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER); + + DockableEditorContainerFactory contentFactory = new DockableEditorContainerFactory(myProject, this, dockManager); + myDockManager.register(DockableEditorContainerFactory.TYPE, contentFactory); + Disposer.register(project, contentFactory); } public static boolean isDumbAware(FileEditor editor) { @@ -172,13 +178,17 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec if (myPanels == null) { myPanels = new JPanel(new BorderLayout()); myPanels.setBorder(new MyBorder()); - mySplitters = new EditorsSplitters(this, true); + 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) { @@ -1098,7 +1108,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return "FileEditorManager"; } - public void initComponent() { /* really do nothing */ } + public void initComponent() { + + } public void disposeComponent() { /* really do nothing */ } diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index fe9df7b53270..5199e05bc7c3 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -28,10 +28,7 @@ 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.DockContainer; -import com.intellij.ui.docking.DockManager; -import com.intellij.ui.docking.DockableContent; -import com.intellij.ui.docking.DragSession; +import com.intellij.ui.docking.*; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,6 +53,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo private Project myProject; + private Map myFactories = new HashMap(); + private Set myContainers = new HashSet(); private Map myWindows = new HashMap(); @@ -70,6 +69,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo private int myWindowIdCounter = 1; + private Element myLoadedState; + public DockManagerImpl(Project project) { myProject = project; } @@ -84,6 +85,17 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo }); } + @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); + } + }); + } + @Override public Set getContainers() { return Collections.unmodifiableSet(myContainers); @@ -276,8 +288,15 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo 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 = content.getContainerFactory().createContainer(); + DockContainer container = getFactory(content.getDockContainerType()).createContainer(); register(container); DockWindow window = new DockWindow(String.valueOf(myWindowIdCounter++), myProject, container); myWindows.put(container, window); @@ -286,7 +305,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo Dimension size = content.getPreferredSize(); Point showPoint = point.getScreenPoint(); showPoint.x -= size.width / 2; - showPoint.y -= size.width / 2; + showPoint.y -= size.height / 2; window.show(); @@ -306,9 +325,9 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo myContainer = container; setProject(project); setComponent(myContainer.getComponent()); + addDisposable(container); IdeEventQueue.getInstance().addPostprocessor(this, this); - Disposer.register(myContainer, this); myContainer.addListener(new DockContainer.Listener.Adapter() { @Override @@ -317,7 +336,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo @Override public void run() { if (myContainer.isEmpty()) { - Disposer.dispose(myContainer); + close(); } } }); @@ -338,9 +357,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo @Override public void dispose() { super.dispose(); - myWindows.remove(myContainer); - Disposer.dispose(myContainer); } @Override @@ -377,7 +394,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo Element eachWindowElement = new Element("window"); eachWindowElement.setAttribute("id", eachWindow.myId); Element content = new Element("content"); - content.setAttribute("loadMethod", eachContainer.getLoadStateMethod()); + content.setAttribute("type", eachContainer.getDockContainerType()); content.addContent(eachContainer.getState()); eachWindowElement.addContent(content); @@ -390,5 +407,6 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo @Override public void loadState(Element state) { + myLoadedState = state; } } From 4c76bf8fd516a3734d7121399ae6f84be6feb050 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 28 Oct 2010 12:57:18 +0400 Subject: [PATCH 15/36] GitCommit: misprint in method name. --- plugins/git4idea/src/git4idea/history/browser/GitCommit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/history/browser/GitCommit.java b/plugins/git4idea/src/git4idea/history/browser/GitCommit.java index 927f4c0ef946..bc6fbbc49605 100644 --- a/plugins/git4idea/src/git4idea/history/browser/GitCommit.java +++ b/plugins/git4idea/src/git4idea/history/browser/GitCommit.java @@ -140,7 +140,7 @@ public class GitCommit { return myAuthorEmail; } - public String getComitterEmail() { + public String getCommitterEmail() { return myComitterEmail; } From 116757767f6a5b811013845c8f4cb1154753f825 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 28 Oct 2010 12:59:08 +0400 Subject: [PATCH 16/36] Git tests framework enhancement: divided to GitSingleUserTestCase and GitCollaborativeTestCase, which has 3 repositories (parent and 2 developers). Fixed several test failures. --- ...angeListManagerUpdateOnFileChangeTest.java | 4 +- .../git4idea/tests/GitChangeProviderTest.java | 20 ++++-- .../tests/GitCollaborativeTestCase.java | 49 +++++++++++++ .../tests/GitHistoryUtilsTestCase.java | 23 ++++-- .../git4idea/tests/GitSingleUserTestCase.java | 38 ++++++++++ .../tests/git4idea/tests/GitTestCase.java | 35 ++++++--- .../git4idea/tests/GitTestRepository.java | 72 ++++++++++++++++--- 7 files changed, 204 insertions(+), 37 deletions(-) create mode 100644 plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java create mode 100644 plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java diff --git a/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java b/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java index 0ee5ecfcfd0d..c64512dc9033 100644 --- a/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java +++ b/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java @@ -15,7 +15,6 @@ */ package git4idea.tests; -import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vfs.VirtualFile; import org.testng.annotations.BeforeMethod; @@ -38,7 +37,7 @@ import static org.testng.Assert.assertTrue; * TODO: This shouldn't rely on Git. Either test all version controls, either test a mock version control. Or even use both approaches. * @author Kirill Likhodedov */ -public class ChangeListManagerUpdateOnFileChangeTest extends GitTestCase { +public class ChangeListManagerUpdateOnFileChangeTest extends GitSingleUserTestCase { private ChangeListManagerImpl myChangeListManager; private VirtualFile afile; @@ -54,6 +53,7 @@ public class ChangeListManagerUpdateOnFileChangeTest extends GitTestCase { assertInChangeList(afile.getPath()); myRepo.commit("commit Message"); myChangeListManager.ensureUpToDate(false); + myRepo.refresh(); } @Test diff --git a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java index 6844fd67d6b4..a4a34b24cca1 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java @@ -23,13 +23,13 @@ import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.vcs.MockChangelistBuilder; +import com.intellij.ui.GuiUtils; import git4idea.GitVcs; import git4idea.changes.GitChangeProvider; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; -import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -45,7 +45,7 @@ import static org.testng.Assert.*; * 3. Calls ChangeProvider.getChanges() and checks that the changes are there. * @author Kirill Likhodedov */ -public class GitChangeProviderTest extends GitTestCase { +public class GitChangeProviderTest extends GitSingleUserTestCase { private GitChangeProvider myChangeProvider; private VcsModifiableDirtyScope myDirtyScope; @@ -91,9 +91,15 @@ public class GitChangeProviderTest extends GitTestCase { @Test public void testDeleteDirRecursively() throws Exception { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override public void run() { - FileUtil.delete(new File(myRepo.getDir().getPath(), "dir")); + GuiUtils.runOrInvokeAndWait(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + FileUtil.delete(new File(myRepo.getDir().getPath(), "dir")); + } + }); } }); assertChanges(new VirtualFile[] { myFiles.get("dir/c.txt"), myFiles.get("dir/subdir/d.txt") }, new FileStatus[] { DELETED, DELETED }); @@ -169,7 +175,7 @@ public class GitChangeProviderTest extends GitTestCase { assertChanges(newFile, FileStatus.MERGED_WITH_CONFLICTS); } - private void modifyFileInBranches(String filename, FileAction masterAction, FileAction featureAction) throws IOException { + private void modifyFileInBranches(String filename, FileAction masterAction, FileAction featureAction) throws Exception { myRepo.createBranch("feature"); performActionOnFileAndRecordToIndex(filename, "feature", featureAction); myRepo.commit(); @@ -184,7 +190,7 @@ public class GitChangeProviderTest extends GitTestCase { CREATE, MODIFY, DELETE, RENAME } - private void performActionOnFileAndRecordToIndex(String filename, String branchName, FileAction action) throws IOException { + private void performActionOnFileAndRecordToIndex(String filename, String branchName, FileAction action) throws Exception { VirtualFile file = myRepo.getDir().findChild(filename); switch (action) { case CREATE: diff --git a/plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java new file mode 100644 index 000000000000..4c21c4fa32ab --- /dev/null +++ b/plugins/git4idea/tests/git4idea/tests/GitCollaborativeTestCase.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea.tests; + +/** + * + * @author Kirill Likhodedov + */ +public class GitCollaborativeTestCase extends GitTestCase { + public static final String MAIN_USER_NAME = "John Smith"; + public static final String MAIN_USER_EMAIL = "john.smith@email.com"; + public static final String BROTHER_USER_NAME = "Bob Doe"; + public static final String BROTHER_USER_EMAIL = "bob.doe@email.com"; + + protected GitTestRepository myRepo; // main repository with IDEA project + protected GitTestRepository myParentRepo; // bare 'central' repository + protected GitTestRepository myBrotherRepo; // another developers repository + + @Override + protected GitTestRepository initRepositories() throws Exception { + myParentRepo = GitTestRepository.create(this); + myRepo = GitTestRepository.cloneFrom(myParentRepo); + myRepo.setName(MAIN_USER_NAME, MAIN_USER_EMAIL); + myBrotherRepo = GitTestRepository.cloneFrom(myParentRepo); + myBrotherRepo.setName(BROTHER_USER_NAME, BROTHER_USER_EMAIL); + return myRepo; + } + + @Override + protected void tearDownRepositories() throws Exception { + myRepo.getDirFixture().tearDown(); + myParentRepo.getDirFixture().tearDown(); + myBrotherRepo.getDirFixture().tearDown(); + } + +} diff --git a/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java index f2d00123616e..f9e449cfb6e8 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java +++ b/plugins/git4idea/tests/git4idea/tests/GitHistoryUtilsTestCase.java @@ -46,7 +46,7 @@ import static org.testng.Assert.*; * * @author Kirill Likhodedov */ -public class GitHistoryUtilsTestCase extends GitTestCase { +public class GitHistoryUtilsTestCase extends GitSingleUserTestCase { private VirtualFile afile; private FilePath bfilePath; @@ -124,7 +124,7 @@ public class GitHistoryUtilsTestCase extends GitTestCase { parents = ArrayUtil.EMPTY_STRING_ARRAY; } final GitTestRevision revision = new GitTestRevision(details[0], details[1], parents, commitMessages[i], - String.format("%s <%s>", CONFIG_USER_NAME, CONFIG_USER_EMAIL), null, + CONFIG_USER_NAME, CONFIG_USER_EMAIL, CONFIG_USER_NAME, CONFIG_USER_EMAIL, null, contents[i]); myRevisions.add(revision); if (i > RENAME_COMMIT_INDEX) { @@ -280,14 +280,17 @@ public class GitHistoryUtilsTestCase extends GitTestCase { assertEquals(((GitRevisionNumber) actual.getRevisionNumber()).getTimestamp(), expected.myDate); // TODO: whitespaces problem is known, remove convertWhitespaces... when it's fixed assertEquals(convertWhitespacesToSpacesAndRemoveDoubles(actual.getCommitMessage()), convertWhitespacesToSpacesAndRemoveDoubles(expected.myCommitMessage)); - assertEquals(actual.getAuthor(), expected.myAuthor); + assertEquals(actual.getAuthor(), expected.myAuthorName); assertEquals(actual.getBranchName(), expected.myBranchName); assertEquals(actual.getContent(), expected.myContent); } private static void assertCommitEqualToTestRevision(GitCommit commit, GitTestRevision expected) throws IOException { assertEquals(commit.getHash().toString(), expected.myHash); - assertEquals( String.format("%s <%s>", commit.getAuthor(), commit.getAuthorEmail()), expected.myAuthor); + assertEquals(commit.getAuthor(), expected.myAuthorName); + assertEquals(commit.getAuthorEmail(), expected.myAuthorEmail); + assertEquals(commit.getCommitter(), expected.myCommitterName); + assertEquals(commit.getCommitterEmail(), expected.myCommitterEmail); assertEquals(commit.getDate(), expected.myDate); assertEquals(convertWhitespacesToSpacesAndRemoveDoubles(commit.getDescription()), convertWhitespacesToSpacesAndRemoveDoubles(expected.myCommitMessage)); assertEqualHashes(commit.getParentsHashes(), Arrays.asList(expected.myParents)); @@ -317,17 +320,23 @@ public class GitHistoryUtilsTestCase extends GitTestCase { final String myHash; final Date myDate; final String myCommitMessage; - final String myAuthor; + final String myAuthorName; + final String myAuthorEmail; + final String myCommitterName; + final String myCommitterEmail; final String myBranchName; final byte[] myContent; private String[] myParents; - public GitTestRevision(String hash, String gitTimestamp, String[] parents, String commitMessage, String author, String branch, String content) { + public GitTestRevision(String hash, String gitTimestamp, String[] parents, String commitMessage, String authorName, String authorEmail, String committerName, String committerEmail, String branch, String content) { myHash = hash; myDate = gitTimeStampToDate(gitTimestamp); myParents = parents; myCommitMessage = commitMessage; - myAuthor = author; + myAuthorName = authorName; + myAuthorEmail = authorEmail; + myCommitterName = committerName; + myCommitterEmail = committerEmail; myBranchName = branch; myContent = content.getBytes(); } diff --git a/plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java new file mode 100644 index 000000000000..89e65d1aba36 --- /dev/null +++ b/plugins/git4idea/tests/git4idea/tests/GitSingleUserTestCase.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea.tests; + +/** + * @author Kirill Likhodedov + */ +public class GitSingleUserTestCase extends GitTestCase { + public static final String CONFIG_USER_NAME = "John Smith"; + public static final String CONFIG_USER_EMAIL = "john.smith@email.com"; + + protected GitTestRepository myRepo; + + @Override + protected GitTestRepository initRepositories() throws Exception { + myRepo = GitTestRepository.create(this); + myRepo.setName(CONFIG_USER_NAME, CONFIG_USER_EMAIL); + return myRepo; + } + + @Override + protected void tearDownRepositories() throws Exception { + myRepo.getDirFixture().tearDown(); + } +} diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitTestCase.java index 3bf58e14122c..591aa22ef642 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestCase.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestCase.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.testFramework.AbstractVcsTestCase; +import com.intellij.ui.GuiUtils; import git4idea.GitVcs; import org.jetbrains.annotations.Nullable; import org.testng.annotations.AfterMethod; @@ -37,14 +38,12 @@ import java.io.IOException; * contains git executable. * @author Kirill Likhodedov */ -public class GitTestCase extends AbstractVcsTestCase { +public abstract class GitTestCase extends AbstractVcsTestCase { public static final String GIT_EXECUTABLE_PATH = "IDEA_TEST_GIT_EXECUTABLE_PATH"; - public static final String CONFIG_USER_NAME = "Git TestCase Name"; - public static final String CONFIG_USER_EMAIL = "Git TestCase Email"; private static final String GIT_EXECUTABLE = (SystemInfo.isWindows ? "git.exe" : "git"); - protected GitTestRepository myRepo; + protected GitTestRepository myMainRepo; private File myProjectDir; @BeforeMethod @@ -59,11 +58,9 @@ public class GitTestCase extends AbstractVcsTestCase { myClientBinaryPath = new File(pluginRoot, "tests/git4idea/tests/data/bin"); } - myRepo = GitTestRepository.create(this); - myRepo.config("user.name", CONFIG_USER_NAME); - myRepo.config("user.email", CONFIG_USER_EMAIL); - myProjectDir = new File(myRepo.getDirFixture().getTempDirPath()); + myMainRepo = initRepositories(); + myProjectDir = new File(myMainRepo.getDirFixture().getTempDirPath()); if (EventQueue.isDispatchThread()) { initProject(myProjectDir); } else { @@ -86,10 +83,28 @@ public class GitTestCase extends AbstractVcsTestCase { doActionSilently(VcsConfiguration.StandardConfirmation.REMOVE); } + /** + * Different implementations for {@link git4idea.tests.GitSingleUserTestCase} and {@link git4idea.tests.GitCollaborativeTestCase}: + * create a single or several repositories, which will be used in tests. + * @return main repository which IDEA project will be bound to. + */ + protected abstract GitTestRepository initRepositories() throws Exception; + + protected abstract void tearDownRepositories() throws Exception; + @AfterMethod protected void tearDown() throws Exception { - tearDownProject(); - myRepo.getDirFixture().tearDown(); + GuiUtils.runOrInvokeAndWait(new Runnable() { + @Override + public void run() { + try { + tearDownProject(); + tearDownRepositories(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); } /** diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java b/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java index 93297f60c279..ceb131bc2519 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java @@ -22,12 +22,16 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.AbstractVcsTestCase; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TempDirTestFixture; +import com.intellij.ui.GuiUtils; +import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.concurrent.atomic.AtomicReference; +import static org.testng.Assert.fail; + /** * Representation of a Git repository for tests purposes. * @author Kirill Likhodedov @@ -35,13 +39,13 @@ import java.util.concurrent.atomic.AtomicReference; public class GitTestRepository { @NotNull private final GitTestCase myTest; @NotNull private final TempDirTestFixture myDirFixture; + private VirtualFile myDir; - public GitTestRepository(@NotNull GitTestCase test, @NotNull TempDirTestFixture dir) { + public GitTestRepository(@NotNull GitTestCase test, @NotNull TempDirTestFixture fixture) { myTest = test; - myDirFixture = dir; + myDirFixture = fixture; } - /** * Creates a new Mercurial repository in a new temporary test directory. * @param testCase reference to the test case instance. @@ -55,6 +59,14 @@ public class GitTestRepository { return new GitTestRepository(testCase, dirFixture); } + public static GitTestRepository cloneFrom(GitTestRepository parent) throws Exception { + final TempDirTestFixture dirFixture = createFixtureDir(); + final File repo = new File(dirFixture.getTempDirPath()); + final ProcessOutput processOutput = parent.getTest().executeCommand(repo, "clone", parent.getDir().getPath(), repo.getPath()); + AbstractVcsTestCase.verify(processOutput); + return new GitTestRepository(parent.getTest(), dirFixture); + } + private static TempDirTestFixture createFixtureDir() throws Exception { final TempDirTestFixture fixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture(); fixture.setUp(); @@ -66,8 +78,20 @@ public class GitTestRepository { return myDirFixture; } + @Nullable public VirtualFile getDir() { - return myDirFixture.getFile("."); + if (myDir == null) { + myDir = VcsUtil.getVirtualFile(myDirFixture.getTempDirPath()); + } + return myDir; + } + + /** + * Configures name and email for this git repository. + */ + public void setName(String name, String email) throws IOException { + config("user.name", name); + config("user.email", email); } /** @@ -175,15 +199,21 @@ public class GitTestRepository { execute(true, "mv", file.getPath(), newPath); } - public void mv(String oldPath, String newPath) throws IOException { + public void mv(String oldPath, String newPath) throws Exception { execute(true, "mv", oldPath, newPath); refreshFile(getDir(), true); } - private static void refreshFile(final VirtualFile file, final boolean recursive) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override public void run() { - file.refresh(false, recursive); + private static void refreshFile(final VirtualFile file, final boolean recursive) throws Exception { + GuiUtils.runOrInvokeAndWait(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + file.refresh(false, recursive); + } + }); } }); } @@ -196,7 +226,7 @@ public class GitTestRepository { execute(true, "push"); } - public void rm(String... filenames) throws IOException { + public void rm(String... filenames) throws Exception { execute(true, join("rm", filenames)); refreshFile(getDir(), true); } @@ -255,8 +285,28 @@ public class GitTestRepository { public void refresh() { ApplicationManager.getApplication().invokeAndWait(new Runnable() { public void run() { - refreshFile(getDir(), true); + try { + refreshFile(getDir(), true); + } catch (Exception e) { + e.printStackTrace(); + fail("Exception while refreshing repository", e); + } } }, ModalityState.defaultModalityState()); } + + public void addRemotes(String... parameters) throws IOException { + for (String s : parameters) { + execute(true, "remote", "add", s); + } + } + + public void branch() throws IOException { + execute(false, "branch"); + } + + @NotNull + public GitTestCase getTest() { + return myTest; + } } From 28ab702bb6b7464038ee9ae618dcc8aa024e4acc Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Thu, 28 Oct 2010 14:11:05 +0400 Subject: [PATCH 17/36] Using correct bundle entry for 'align fields in column' formatting setting --- .../intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java index a565eeecfa22..e5862741a6f6 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java @@ -49,7 +49,7 @@ public interface CodeStyleSettingsCustomizable { String WRAPPING_THROWS_KEYWORD = ApplicationBundle.message("wrapping.throws.keyword"); String WRAPPING_TERNARY_OPERATION = ApplicationBundle.message("wrapping.ternary.operation"); String WRAPPING_ASSIGNMENT = ApplicationBundle.message("wrapping.assignment.statement"); - String WRAPPING_FIELDS_VARIABLES_GROUPS = ApplicationBundle.message("wrapping.assignment.variables.groups"); + String WRAPPING_FIELDS_VARIABLES_GROUPS = ApplicationBundle.message("checkbox.align.multiline.fields.groups"); String WRAPPING_ARRAY_INITIALIZER = ApplicationBundle.message("wrapping.array.initializer"); String WRAPPING_MODIFIER_LIST = ApplicationBundle.message("wrapping.modifier.list"); String WRAPPING_ASSERT_STATEMENT = ApplicationBundle.message("wrapping.assert.statement"); From 4468a95ddeb0f42dcb921014be87439a0360534b Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Thu, 28 Oct 2010 14:11:55 +0400 Subject: [PATCH 18/36] add visibility option in LineAnnotationAspect --- .../src/messages/VcsBundle.properties | 3 ++ .../vcs/annotate/LineAnnotationAspect.java | 39 +++++++++++++++---- .../annotate/LineAnnotationAspectAdapter.java | 29 ++++++++++++++ .../annotate/MajorLineAnnotationAspect.java | 25 ------------ .../src/com/intellij/vcsUtil/VcsUtil.java | 13 +++++++ .../vcs/actions/AnnotationFieldGutter.java | 8 +++- .../annotate/CvsFileAnnotation.java | 13 ++++--- .../git4idea/annotate/GitFileAnnotation.java | 13 ++++--- .../provider/annotate/HgAnnotation.java | 20 +++++++--- .../jetbrains/idea/svn/SvnBundle.properties | 3 +- .../idea/svn/annotate/SvnFileAnnotation.java | 16 ++++---- 11 files changed, 122 insertions(+), 60 deletions(-) delete mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java diff --git a/platform/platform-resources-en/src/messages/VcsBundle.properties b/platform/platform-resources-en/src/messages/VcsBundle.properties index 82b68f6c7574..72402ea1a7e9 100644 --- a/platform/platform-resources-en/src/messages/VcsBundle.properties +++ b/platform/platform-resources-en/src/messages/VcsBundle.properties @@ -541,3 +541,6 @@ options.background.processes.title=Background processes options.background.processes.enable=Enable background processes options.check.changed.on.server.title="Changed on server" conflicts revision.changes.not.supported=Current VCS does not support viewing changes by revision +line.annotation.aspect.author=Author +line.annotation.aspect.date=Date +line.annotation.aspect.revision=Revision diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java index 7b14c09c9f56..33b27700f7c7 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspect.java @@ -15,26 +15,51 @@ */ package com.intellij.openapi.vcs.annotate; +import com.intellij.openapi.vcs.VcsBundle; import org.jetbrains.annotations.Nullable; /** * Represents one part of a line annotation which is shown in the editor when the "Annotate" * action is invoked. Classes implementing this interface can also implement - * {@link com.intellij.openapi.editor.EditorGutterAction} to handle clicks on the annotation. + * {@link com.intellij.openapi.editor.EditorGutterAction} to handle clicks on the annotation. * + * @author Konstantin Bulenkov * @see FileAnnotation#getAspects() */ public interface LineAnnotationAspect { + String AUTHOR = VcsBundle.message("line.annotation.aspect.author"); + String DATE = VcsBundle.message("line.annotation.aspect.date"); + String REVISION = VcsBundle.message("line.annotation.aspect.revision"); /** - * Get annotation text for the spcific line number - * @param lineNumber the line number to query - * @return the annotation text + * Get annotation text for the specific line number + * + * @param line the line number to query + * @return the annotation text */ - String getValue(int lineNumber); + String getValue(int line); /** - * used if return value is not null + * Used to show a tooltip for specific line or group of lines + * + * @param line the line number to query + * @return the tooltip text for the line */ @Nullable - String getTooltipText(int lineNumber); + String getTooltipText(int line); + + /** + * Returns unique identifier, that will be used to show/hide some aspects + * If null this line aspect won't be configurable in annotation settings + * + * @return unique id + */ + @Nullable + String getId(); + + /** + * Returns true if this aspect will be shown on Annotate action + * + * @return true if this aspect will be shown on Annotate action + */ + boolean isShowByDefault(); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java index 7fca123359ba..700adf41a409 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/LineAnnotationAspectAdapter.java @@ -19,11 +19,40 @@ import com.intellij.openapi.editor.EditorGutterAction; import java.awt.*; +/** + * @author Konstantin Bulenkov + */ public abstract class LineAnnotationAspectAdapter implements LineAnnotationAspect, EditorGutterAction { + private final String myId; + private final boolean myShowByDefault; + + protected LineAnnotationAspectAdapter() { + this(null, false); + } + + protected LineAnnotationAspectAdapter(String id) { + this(id, false); + } + + public LineAnnotationAspectAdapter(String id, boolean showByDefault) { + myId = id; + myShowByDefault = showByDefault; + } + public String getTooltipText(int lineNumber) { return null; } + @Override + public String getId() { + return myId; + } + + @Override + public boolean isShowByDefault() { + return myShowByDefault; + } + public Cursor getCursor(final int lineNum) { return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java deleted file mode 100644 index 5594bcd23012..000000000000 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/MajorLineAnnotationAspect.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.vcs.annotate; - -/** - * Markup interface for aspects always visible in the gutter. Other aspects will be visible only after - * Show Details action - * - * @author Konstantin Bulenkov - */ -public interface MajorLineAnnotationAspect extends LineAnnotationAspect { -} diff --git a/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java b/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java index c450e35b7b8c..9eea6b38d768 100644 --- a/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java +++ b/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.vcsUtil; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.Application; @@ -33,6 +34,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.actions.VcsContextFactory; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; @@ -579,4 +581,15 @@ public class VcsUtil { return (! s1Trimmed.equals(s2Trimmed)) && s1Trimmed.equalsIgnoreCase(s2Trimmed); } + + private static String ANNO_ASPECT = "show.vcs.annotation.aspect."; + public static boolean isAspectAvailableByDefault(LineAnnotationAspect aspect) { + if (aspect.getId() == null) return aspect.isShowByDefault(); + return PropertiesComponent.getInstance().getBoolean(ANNO_ASPECT + aspect.getId(), aspect.isShowByDefault()); + } + + public static void setAspectAvailability(LineAnnotationAspect aspect, boolean showByDefault) { + if (aspect.getId() == null) throw new IllegalArgumentException("Aspect should have not null ID"); + PropertiesComponent.getInstance().setValue(ANNO_ASPECT + aspect.getId(), String.valueOf(showByDefault)); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java index 8b1a75279420..88a025d5368c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java @@ -20,8 +20,12 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorGutterAction; import com.intellij.openapi.editor.colors.ColorKey; import com.intellij.openapi.editor.colors.EditorFontType; -import com.intellij.openapi.vcs.annotate.*; +import com.intellij.openapi.vcs.annotate.AnnotationListener; +import com.intellij.openapi.vcs.annotate.FileAnnotation; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; +import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.vcsUtil.VcsUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.Nullable; @@ -148,6 +152,6 @@ class AnnotationFieldGutter implements ActiveAnnotationGutter { } private boolean isAvailable() { - return myShowAdditionalInfo || myAspect instanceof MajorLineAnnotationAspect; + return myShowAdditionalInfo || VcsUtil.isAspectAvailableByDefault(myAspect); } } diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java index 7a3dc2e6d13d..4a699b857442 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java @@ -37,7 +37,7 @@ public class CvsFileAnnotation implements FileAnnotation{ private final VirtualFile myFile; private final List myListeners = new ArrayList(); - private final LineAnnotationAspect USER = new CvsUserAnnotationAspect() { + private final LineAnnotationAspect USER = new CvsAnnotationAspect(CvsAnnotationAspect.AUTHOR, true) { public String getValue(int lineNumber) { if (lineNumber < 0 || lineNumber >= myAnnotations.length) { return ""; @@ -48,7 +48,7 @@ public class CvsFileAnnotation implements FileAnnotation{ } }; - private final LineAnnotationAspect DATE = new CvsAnnotationAspect() { + private final LineAnnotationAspect DATE = new CvsAnnotationAspect(CvsAnnotationAspect.DATE, true) { public String getValue(int lineNumber) { if (lineNumber < 0 || lineNumber >= myAnnotations.length) { return ""; @@ -59,7 +59,7 @@ public class CvsFileAnnotation implements FileAnnotation{ } }; - private final LineAnnotationAspect REVISION = new CvsAnnotationAspect() { + private final LineAnnotationAspect REVISION = new CvsAnnotationAspect(CvsAnnotationAspect.REVISION, false) { public String getValue(int lineNumber) { if (lineNumber < 0 || lineNumber >= myAnnotations.length) { return ""; @@ -173,12 +173,13 @@ public class CvsFileAnnotation implements FileAnnotation{ } private abstract class CvsAnnotationAspect extends LineAnnotationAspectAdapter { + public CvsAnnotationAspect(String id, boolean showByDefault) { + super(id, showByDefault); + } + @Override protected void showAffectedPaths(int lineNum) { //todo } } - - private abstract class CvsUserAnnotationAspect extends CvsAnnotationAspect implements MajorLineAnnotationAspect { - } } diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index 3f01599ea6e3..197586c81b46 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -81,14 +81,14 @@ public class GitFileAnnotation implements FileAnnotation { */ private final boolean myMonitorFlag; - private final LineAnnotationAspect DATE_ASPECT = new GitAnnotationAspect() { + private final LineAnnotationAspect DATE_ASPECT = new GitAnnotationAspect(GitAnnotationAspect.DATE, true) { public String doGetValue(LineInfo info) { final Date date = info.getDate(); return date == null ? "" : DateFormatUtil.formatPrettyDate(date); } }; - private final LineAnnotationAspect REVISION_ASPECT = new GitAnnotationAspect() { + private final LineAnnotationAspect REVISION_ASPECT = new GitAnnotationAspect(GitAnnotationAspect.REVISION, false) { @Override protected String doGetValue(LineInfo lineInfo) { final GitRevisionNumber revision = lineInfo.getRevision(); @@ -96,7 +96,7 @@ public class GitFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect AUTHOR_ASPECT = new GitAuthorAnnotationAspect() { + private final LineAnnotationAspect AUTHOR_ASPECT = new GitAnnotationAspect(GitAnnotationAspect.AUTHOR, true) { @Override protected String doGetValue(LineInfo lineInfo) { final String author = lineInfo.getAuthor(); @@ -278,6 +278,10 @@ public class GitFileAnnotation implements FileAnnotation { * Revision annotation aspect implementation */ private abstract class GitAnnotationAspect extends LineAnnotationAspectAdapter { + public GitAnnotationAspect(String id, boolean showByDefault) { + super(id, showByDefault); + } + public String getValue(int lineNumber) { if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) { return ""; @@ -301,9 +305,6 @@ public class GitFileAnnotation implements FileAnnotation { } } - private abstract class GitAuthorAnnotationAspect extends GitAnnotationAspect implements MajorLineAnnotationAspect { - } - /** * Line information */ diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java index 1cf807c422e4..02addf7afa86 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java @@ -105,10 +105,24 @@ public class HgAnnotation implements FileAnnotation { return result; } + private static String id(FIELD field) { + switch (field) { + case USER: return LineAnnotationAspect.AUTHOR; + case REVISION: return LineAnnotationAspect.REVISION; + case DATE: return LineAnnotationAspect.DATE; + default: return null; + } + } + + private static boolean isShowByDefault(FIELD aspectType) { + return aspectType == FIELD.DATE || aspectType == FIELD.USER; + } + class HgLineAnnotationAspect extends LineAnnotationAspectAdapter { private final FIELD aspectType; public HgLineAnnotationAspect(FIELD aspectType) { + super(id(aspectType), HgAnnotation.isShowByDefault(aspectType)); this.aspectType = aspectType; } @@ -128,12 +142,6 @@ public class HgAnnotation implements FileAnnotation { } } - class HgUserAnnotationAspect extends HgLineAnnotationAspect implements MajorLineAnnotationAspect { - public HgUserAnnotationAspect(FIELD aspectType) { - super(aspectType); - } - } - public boolean revisionsNotEmpty() { return true; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index 30d7b8d0d35f..8c710fa66bd8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -617,4 +617,5 @@ action.mark.tree.conflict.resolved.confirmation.title=Mark Tree Conflict Resolve svn.integrate.changelist.warning.unresolved.conflicts.text=Integration interrupted. There are unresolved conflicts or skipped items. svn.option.ignore.whitespace.in.annotate=Ignore whitespace differences in annotations annotation.show.merge.sources.default.text=Show merge source in annotations -svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no" \ No newline at end of file +svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no" +annotation.original.revision=Original revision \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java index c6697614bdae..d711a362dd29 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java @@ -23,6 +23,7 @@ import com.intellij.util.text.DateFormatUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnEntriesListener; import org.jetbrains.idea.svn.SvnVcs; @@ -40,7 +41,7 @@ public class SvnFileAnnotation implements FileAnnotation { private final List myListeners = new ArrayList(); private final Map myRevisionMap = new HashMap(); - private final LineAnnotationAspect DATE_ASPECT = new SvnAnnotationAspect() { + private final LineAnnotationAspect DATE_ASPECT = new SvnAnnotationAspect(SvnAnnotationAspect.DATE, true) { public String getValue(int lineNumber) { if (myInfos.size() <= lineNumber || lineNumber < 0) { return ""; @@ -52,7 +53,7 @@ public class SvnFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect REVISION_ASPECT = new SvnAnnotationAspect() { + private final LineAnnotationAspect REVISION_ASPECT = new SvnAnnotationAspect(SvnAnnotationAspect.REVISION, false) { public String getValue(int lineNumber) { if (myInfos.size() <= lineNumber || lineNumber < 0) { return ""; @@ -64,7 +65,7 @@ public class SvnFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect ORIGINAL_REVISION_ASPECT = new SvnAnnotationAspect() { + private final LineAnnotationAspect ORIGINAL_REVISION_ASPECT = new SvnAnnotationAspect(SvnBundle.message("annotation.original.revision"), false) { @Override public String getValue(int lineNumber) { final long value = myInfos.originalRevision(lineNumber); @@ -92,7 +93,7 @@ public class SvnFileAnnotation implements FileAnnotation { } }; - private final LineAnnotationAspect AUTHOR_ASPECT = new SvnAuthorAnnotationAspect() { + private final LineAnnotationAspect AUTHOR_ASPECT = new SvnAnnotationAspect(SvnAnnotationAspect.AUTHOR, true) { public String getValue(int lineNumber) { if (myInfos.size() <= lineNumber || lineNumber < 0) { return ""; @@ -290,6 +291,10 @@ public class SvnFileAnnotation implements FileAnnotation { } private abstract class SvnAnnotationAspect extends LineAnnotationAspectAdapter { + public SvnAnnotationAspect(String id, boolean showByDefault) { + super(id, showByDefault); + } + protected long getRevision(final int lineNum) { final LineInfo lineInfo = myInfos.get(lineNum); return (lineInfo == null) ? -1 : lineInfo.getRevision(); @@ -306,9 +311,6 @@ public class SvnFileAnnotation implements FileAnnotation { } } - private abstract class SvnAuthorAnnotationAspect extends SvnAnnotationAspect implements MajorLineAnnotationAspect { - } - private static class MyPartiallyCreatedInfos { private boolean myShowMergeSource; private final Map myMappedLineInfo; From d82d724515c8f713bd42bab470c841a2fdf8a7e0 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Thu, 28 Oct 2010 14:31:23 +0400 Subject: [PATCH 19/36] editor detach: read/write state --- .../ui/docking/DockContainerFactory.java | 2 +- .../impl/DockableEditorContainerFactory.java | 11 ++++- .../impl/DockableEditorTabbedContainer.java | 2 +- .../impl/FileEditorManagerImpl.java | 13 ++++-- .../ui/docking/impl/DockManagerImpl.java | 43 +++++++++++++++++-- 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java index f802af0b23e7..3dcc059e3de2 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java @@ -24,7 +24,7 @@ public interface DockContainerFactory extends Disposable { interface Persistent extends DockContainerFactory { - void loadState(Element element); + DockContainer loadContainerFrom(Element element); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java index e70e83054244..47fcd60b97fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java @@ -21,8 +21,9 @@ 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 { +public class DockableEditorContainerFactory implements DockContainerFactory.Persistent { public static final String TYPE = "file-editors"; @@ -55,6 +56,14 @@ public class DockableEditorContainerFactory implements DockContainerFactory { return container.get(); } + @Override + public DockContainer loadContainerFrom(Element element) { + DockableEditorTabbedContainer container = (DockableEditorTabbedContainer)createContainer(); + container.getSplitters().readExternal(element.getChild("state")); + container.getSplitters().openFiles(); + return container; + } + @Override public void dispose() { } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index 9663a43e12fe..aaa63f1f19b0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -69,7 +69,7 @@ class DockableEditorTabbedContainer implements DockContainer.Persistent { @Override public Element getState() { - Element editors = new Element("editors"); + Element editors = new Element("state"); mySplitters.writeExternal(editors); return editors; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 5335157e4364..89a0eaf9267f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -104,16 +104,21 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec */ private final MyEditorPropertyChangeListener myEditorPropertyChangeListener = new MyEditorPropertyChangeListener(); private DockManager myDockManager; + private DockableEditorContainerFactory myContentFactory; public FileEditorManagerImpl(final Project project, DockManager dockManager) { /* ApplicationManager.getApplication().assertIsDispatchThread(); */ myProject = project; myDockManager = dockManager; myListenerList = new MessageListenerList(myProject.getMessageBus(), FileEditorManagerListener.FILE_EDITOR_MANAGER); + } - DockableEditorContainerFactory contentFactory = new DockableEditorContainerFactory(myProject, this, dockManager); - myDockManager.register(DockableEditorContainerFactory.TYPE, contentFactory); - Disposer.register(project, contentFactory); + 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) { @@ -1073,6 +1078,8 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public void run() { setTabsMode(UISettings.getInstance().EDITOR_TAB_PLACEMENT != UISettings.TABS_NONE); getMainSplitters().openFiles(); + initDockableContentFactory(); + LaterInvocator.invokeLater(new Runnable() { public void run() { long currentTime = System.nanoTime(); diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 5199e05bc7c3..80cbd5b80d9f 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -42,6 +42,7 @@ 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", @@ -94,6 +95,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo myFactories.remove(id); } }); + + readStateFor(id); } @Override @@ -298,9 +301,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo private void createNewDockContainerFor(DockableContent content, RelativePoint point) { DockContainer container = getFactory(content.getDockContainerType()).createContainer(); register(container); - DockWindow window = new DockWindow(String.valueOf(myWindowIdCounter++), myProject, container); - myWindows.put(container, window); - window.show(); + + DockWindow window = createWindowFor(null, container); Dimension size = content.getPreferredSize(); Point showPoint = point.getScreenPoint(); @@ -315,6 +317,14 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo 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; @@ -409,4 +419,31 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo 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); + } + } } From f13daca75f772196405efe45ecd4a998a50df899 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 28 Oct 2010 14:31:42 +0400 Subject: [PATCH 20/36] test fixed --- .../codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java index 45dbdf27768e..1aa4e3b06fa6 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlHighlightVisitor.java @@ -76,7 +76,9 @@ public class XmlHighlightVisitor extends XmlElementVisitor implements HighlightV @Override protected Boolean compute(PsiElement parent, Object p) { OuterLanguageElement element = PsiTreeUtil.getChildOfType(parent, OuterLanguageElement.class); - return element != null && element.getLanguage() != parent.getLanguage(); + if (element == null) return false; + PsiFile containingFile = parent.getContainingFile(); + return containingFile.getViewProvider().getBaseLanguage() != containingFile.getLanguage(); } }; private List myResult; From 81acfa20dbd2fd0876ba387badb7750723de8fa4 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Thu, 28 Oct 2010 14:43:22 +0400 Subject: [PATCH 21/36] CRLF --- .../git4idea/ssh/GitSSHXmlRpcClient.java | 504 ++-- .../org/jetbrains/git4idea/ssh/SSHMain.java | 1174 ++++---- .../hg4idea/src/org/zmlx/hg4idea/HgVcs.java | 822 +++--- resources/src/META-INF/IdeaPlugin.xml | 2380 ++++++++--------- 4 files changed, 2440 insertions(+), 2440 deletions(-) diff --git a/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHXmlRpcClient.java b/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHXmlRpcClient.java index 43763090185b..ce5650462dc6 100644 --- a/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHXmlRpcClient.java +++ b/plugins/git4idea/src/org/jetbrains/git4idea/ssh/GitSSHXmlRpcClient.java @@ -1,252 +1,252 @@ -/* - * 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 org.jetbrains.git4idea.ssh; - -import org.apache.xmlrpc.XmlRpcClientLite; -import org.apache.xmlrpc.XmlRpcException; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.net.Inet4Address; -import java.util.Vector; - -/** - * Client for IDEA SSH GUI event handler - */ -@SuppressWarnings({"UseOfObsoleteCollectionType"}) -public class GitSSHXmlRpcClient implements GitSSHHandler { - /** - * XML RPC client - */ - @Nullable private final XmlRpcClientLite myClient; - - /** - * A constructor - * - * @param port port number - * @param batchMode if true, the client is run in the batch mode, so nothing should be prompted - * @throws IOException if there is IO problem - */ - GitSSHXmlRpcClient(final int port, final boolean batchMode) throws IOException { - //noinspection HardCodedStringLiteral - myClient = batchMode ? null : new XmlRpcClientLite(Inet4Address.getLocalHost().getHostAddress(), port); - } - - /** - * {@inheritDoc} - */ - @SuppressWarnings("unchecked") - public boolean verifyServerHostKey(final int handler, - final String hostname, - final int port, - final String serverHostKeyAlgorithm, - final String serverHostKey, - final boolean isNew) { - if (myClient == null) { - return false; - } - Vector parameters = new Vector(); - parameters.add(handler); - parameters.add(hostname); - parameters.add(port); - parameters.add(serverHostKeyAlgorithm); - parameters.add(serverHostKey); - parameters.add(isNew); - try { - return ((Boolean)myClient.execute(methodName("verifyServerHostKey"), parameters)).booleanValue(); - } - catch (XmlRpcException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - catch (IOException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - } - - /** - * Get the full method name - * - * @param method short name of the method - * @return full method name - */ - private static String methodName(@NonNls final String method) { - return HANDLER_NAME + "." + method; - } - - /** - * {@inheritDoc} - */ - @Nullable - @SuppressWarnings("unchecked") - public String askPassphrase(final int handler, - final String username, - final String keyPath, - final boolean resetPassword, - final String lastError) { - if (myClient == null) { - return null; - } - Vector parameters = new Vector(); - parameters.add(handler); - parameters.add(username); - parameters.add(keyPath); - parameters.add(resetPassword); - parameters.add(lastError); - try { - return adjustNull(((String)myClient.execute(methodName("askPassphrase"), parameters))); - } - catch (XmlRpcException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - catch (IOException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - } - - /** - * {@inheritDoc} - */ - @Nullable - @SuppressWarnings("unchecked") - public Vector replyToChallenge(final int handlerNo, - final String username, - final String name, - final String instruction, - final int numPrompts, - final Vector prompt, - final Vector echo, - final String lastError) { - if (myClient == null) { - return null; - } - Vector parameters = new Vector(); - parameters.add(handlerNo); - parameters.add(username); - parameters.add(name); - parameters.add(instruction); - parameters.add(numPrompts); - parameters.add(prompt); - parameters.add(echo); - parameters.add(lastError); - try { - return adjustNull((Vector)myClient.execute(methodName("replyToChallenge"), parameters)); - } - catch (XmlRpcException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - catch (IOException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - } - - /** - * {@inheritDoc} - */ - @Nullable - @SuppressWarnings("unchecked") - public String askPassword(final int handlerNo, final String username, final boolean resetPassword, final String lastError) { - if (myClient == null) { - return null; - } - Vector parameters = new Vector(); - parameters.add(handlerNo); - parameters.add(username); - parameters.add(resetPassword); - parameters.add(lastError); - try { - return adjustNull(((String)myClient.execute(methodName("askPassword"), parameters))); - } - catch (XmlRpcException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - catch (IOException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public String setLastSuccessful(int handlerNo, String userName, String method, String error) { - if (myClient == null) { - return ""; - } - Vector parameters = new Vector(); - parameters.add(handlerNo); - parameters.add(userName); - parameters.add(method); - parameters.add(error); - try { - return (String)myClient.execute(methodName("setLastSuccessful"), parameters); - } - catch (XmlRpcException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - catch (IOException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public String getLastSuccessful(int handlerNo, String userName) { - if (myClient == null) { - return ""; - } - Vector parameters = new Vector(); - parameters.add(handlerNo); - parameters.add(userName); - try { - return (String)myClient.execute(methodName("getLastSuccessful"), parameters); - } - catch (XmlRpcException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - catch (IOException e) { - throw new RuntimeException("Invocation failed " + e.getMessage(), e); - } - } - - /** - * Since XML RPC client does not understand null values, the value should be - * adjusted (The password is {@code "-"} if null, {@code "+"+s) if non-null). - * - * @param s a value to adjust - * @return adjusted value. - */ - @Nullable - private static String adjustNull(final String s) { - return s.charAt(0) == '-' ? null : s.substring(1); - } - - /** - * Since XML RPC client does not understand null values, the value should be - * adjusted. This is done by replacing empty array with null. - * - * @param s a value to adjust - * @return adjusted value. - */ - @Nullable - private static Vector adjustNull(final Vector s) { - return s.size() == 0 ? null : s; - } -} +/* + * 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 org.jetbrains.git4idea.ssh; + +import org.apache.xmlrpc.XmlRpcClientLite; +import org.apache.xmlrpc.XmlRpcException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.net.Inet4Address; +import java.util.Vector; + +/** + * Client for IDEA SSH GUI event handler + */ +@SuppressWarnings({"UseOfObsoleteCollectionType"}) +public class GitSSHXmlRpcClient implements GitSSHHandler { + /** + * XML RPC client + */ + @Nullable private final XmlRpcClientLite myClient; + + /** + * A constructor + * + * @param port port number + * @param batchMode if true, the client is run in the batch mode, so nothing should be prompted + * @throws IOException if there is IO problem + */ + GitSSHXmlRpcClient(final int port, final boolean batchMode) throws IOException { + //noinspection HardCodedStringLiteral + myClient = batchMode ? null : new XmlRpcClientLite(Inet4Address.getLocalHost().getHostAddress(), port); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + public boolean verifyServerHostKey(final int handler, + final String hostname, + final int port, + final String serverHostKeyAlgorithm, + final String serverHostKey, + final boolean isNew) { + if (myClient == null) { + return false; + } + Vector parameters = new Vector(); + parameters.add(handler); + parameters.add(hostname); + parameters.add(port); + parameters.add(serverHostKeyAlgorithm); + parameters.add(serverHostKey); + parameters.add(isNew); + try { + return ((Boolean)myClient.execute(methodName("verifyServerHostKey"), parameters)).booleanValue(); + } + catch (XmlRpcException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + catch (IOException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + } + + /** + * Get the full method name + * + * @param method short name of the method + * @return full method name + */ + private static String methodName(@NonNls final String method) { + return HANDLER_NAME + "." + method; + } + + /** + * {@inheritDoc} + */ + @Nullable + @SuppressWarnings("unchecked") + public String askPassphrase(final int handler, + final String username, + final String keyPath, + final boolean resetPassword, + final String lastError) { + if (myClient == null) { + return null; + } + Vector parameters = new Vector(); + parameters.add(handler); + parameters.add(username); + parameters.add(keyPath); + parameters.add(resetPassword); + parameters.add(lastError); + try { + return adjustNull(((String)myClient.execute(methodName("askPassphrase"), parameters))); + } + catch (XmlRpcException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + catch (IOException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + } + + /** + * {@inheritDoc} + */ + @Nullable + @SuppressWarnings("unchecked") + public Vector replyToChallenge(final int handlerNo, + final String username, + final String name, + final String instruction, + final int numPrompts, + final Vector prompt, + final Vector echo, + final String lastError) { + if (myClient == null) { + return null; + } + Vector parameters = new Vector(); + parameters.add(handlerNo); + parameters.add(username); + parameters.add(name); + parameters.add(instruction); + parameters.add(numPrompts); + parameters.add(prompt); + parameters.add(echo); + parameters.add(lastError); + try { + return adjustNull((Vector)myClient.execute(methodName("replyToChallenge"), parameters)); + } + catch (XmlRpcException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + catch (IOException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + } + + /** + * {@inheritDoc} + */ + @Nullable + @SuppressWarnings("unchecked") + public String askPassword(final int handlerNo, final String username, final boolean resetPassword, final String lastError) { + if (myClient == null) { + return null; + } + Vector parameters = new Vector(); + parameters.add(handlerNo); + parameters.add(username); + parameters.add(resetPassword); + parameters.add(lastError); + try { + return adjustNull(((String)myClient.execute(methodName("askPassword"), parameters))); + } + catch (XmlRpcException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + catch (IOException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public String setLastSuccessful(int handlerNo, String userName, String method, String error) { + if (myClient == null) { + return ""; + } + Vector parameters = new Vector(); + parameters.add(handlerNo); + parameters.add(userName); + parameters.add(method); + parameters.add(error); + try { + return (String)myClient.execute(methodName("setLastSuccessful"), parameters); + } + catch (XmlRpcException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + catch (IOException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public String getLastSuccessful(int handlerNo, String userName) { + if (myClient == null) { + return ""; + } + Vector parameters = new Vector(); + parameters.add(handlerNo); + parameters.add(userName); + try { + return (String)myClient.execute(methodName("getLastSuccessful"), parameters); + } + catch (XmlRpcException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + catch (IOException e) { + throw new RuntimeException("Invocation failed " + e.getMessage(), e); + } + } + + /** + * Since XML RPC client does not understand null values, the value should be + * adjusted (The password is {@code "-"} if null, {@code "+"+s) if non-null). + * + * @param s a value to adjust + * @return adjusted value. + */ + @Nullable + private static String adjustNull(final String s) { + return s.charAt(0) == '-' ? null : s.substring(1); + } + + /** + * Since XML RPC client does not understand null values, the value should be + * adjusted. This is done by replacing empty array with null. + * + * @param s a value to adjust + * @return adjusted value. + */ + @Nullable + private static Vector adjustNull(final Vector s) { + return s.size() == 0 ? null : s; + } +} diff --git a/plugins/git4idea/src/org/jetbrains/git4idea/ssh/SSHMain.java b/plugins/git4idea/src/org/jetbrains/git4idea/ssh/SSHMain.java index 8f70b0b8d5b2..6bcac6fdcb6b 100644 --- a/plugins/git4idea/src/org/jetbrains/git4idea/ssh/SSHMain.java +++ b/plugins/git4idea/src/org/jetbrains/git4idea/ssh/SSHMain.java @@ -1,587 +1,587 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.git4idea.ssh; - -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.ArrayUtil; -import com.trilead.ssh2.*; -import com.trilead.ssh2.crypto.PEMDecoder; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.util.*; -import java.util.concurrent.Semaphore; - -/** - * The main class for SSH client. It can only handle the following command line (which is use by GIT): - * git-ssh xmlRpcPort [-p port] host command. The program is wrapped in the script, so XML RPC port - * not settable using script interface. - *

- * The code here is based on SwingShell example. - */ -@SuppressWarnings({"CallToPrintStackTrace", "UseOfSystemOutOrSystemErr"}) -public class SSHMain { - /** - * the semaphore - */ - private final Semaphore myForwardCompleted = new Semaphore(0); - /** - * the host - */ - private final SSHConfig.Host myHost; - /** - * Handler number - */ - private final int myHandlerNo; - /** - * the xml RPC port - */ - private final GitSSHXmlRpcClient myXmlRpcClient; - /** - * the command to run - */ - private final String myCommand; - /** - * the exit code - */ - private int myExitCode = 0; - /** - * The last error - */ - private String myLastError = ""; - /** - * Path to known hosts file - */ - @NonNls private static final String knownHostPath = SSHConfig.USER_HOME + "/.ssh/known_hosts"; - /** - * Path to DSA key - */ - @NonNls private static final String idDSAPath = SSHConfig.USER_HOME + "/.ssh/id_dsa"; - /** - * Path to RSA key - */ - @NonNls private static final String idRSAPath = SSHConfig.USER_HOME + "/.ssh/id_rsa"; - - /** - * database of known hosts - */ - private final KnownHosts database = new KnownHosts(); - /** - * size of the buffers for stream forwarding - */ - private static final int BUFFER_SIZE = 16 * 1024; - /** - * public key authentication method - */ - @NonNls public static final String PUBLIC_KEY_METHOD = "publickey"; - /** - * keyboard interactive method - */ - @NonNls public static final String KEYBOARD_INTERACTIVE_METHOD = "keyboard-interactive"; - /** - * password method - */ - @NonNls public static final String PASSWORD_METHOD = "password"; - /** - * RSA algorithm - */ - @NonNls public static final String SSH_RSA_ALGORITHM = "ssh-rsa"; - /** - * DSS algorithm - */ - @NonNls public static final String SSH_DSS_ALGORITHM = "ssh-dss"; - - - /** - * A constructor - * - * @param host a host - * @param username a name of user (from URL) - * @param port a port - * @param command a command - * @throws IOException if config file could not be loaded - */ - private SSHMain(String host, String username, Integer port, String command) throws IOException { - SSHConfig config = SSHConfig.load(); - myHost = config.lookup(username, host, port); - myHandlerNo = Integer.parseInt(System.getenv(GitSSHHandler.SSH_HANDLER_ENV)); - int xmlRpcPort = Integer.parseInt(System.getenv(GitSSHHandler.SSH_PORT_ENV)); - myXmlRpcClient = new GitSSHXmlRpcClient(xmlRpcPort, myHost.isBatchMode()); - myCommand = command; - } - - /** - * The application entry point - * - * @param args program arguments - */ - public static void main(String[] args) { - try { - SSHMain app = parseArguments(args); - app.start(); - System.exit(app.myExitCode); - } - catch (Throwable t) { - t.printStackTrace(); - System.exit(1); - } - } - - /** - * Start the application - * - * @throws IOException if there is a problem with connection - * @throws InterruptedException if thread was interrupted - */ - private void start() throws IOException, InterruptedException { - Connection c = new Connection(myHost.getHostName(), myHost.getPort()); - try { - configureKnownHosts(c); - c.connect(new HostKeyVerifier()); - authenticate(c); - final Session s = c.openSession(); - try { - s.execCommand(myCommand); - // Note that stdin is not being waited using semaphore. - // Instead, the SSH process waits for remote process exit - // if remote process exited, none is interested in stdin - // anyway. - forward("stdin", s.getStdin(), System.in, false); - forward("stdout", System.out, s.getStdout(), true); - forward("stderr", System.err, s.getStderr(), true); - myForwardCompleted.acquire(2); // wait only for stderr and stdout - s.waitForCondition(ChannelCondition.EXIT_STATUS, Long.MAX_VALUE); - Integer exitStatus = s.getExitStatus(); - if (exitStatus == null) { - // broken exit status - exitStatus = 1; - } - System.exit(exitStatus.intValue() == 0 ? myExitCode : exitStatus.intValue()); - } - finally { - s.close(); - } - } - finally { - c.close(); - } - } - - /** - * Authenticate using some supported methods. If authentication fails, - * the method throws {@link IOException}. - * - * @param c the connection to use for authentication - * @throws IOException in case of IO error or authentication failure - */ - private void authenticate(final Connection c) throws IOException { - LinkedList methods = new LinkedList(myHost.getPreferredMethods()); - String lastSuccessfulMethod = myXmlRpcClient.getLastSuccessful(myHandlerNo, getUserHostString()); - if (lastSuccessfulMethod != null && lastSuccessfulMethod.length() > 0 && methods.remove(lastSuccessfulMethod)) { - methods.addFirst(lastSuccessfulMethod); - } - for (String method : methods) { - if (c.isAuthenticationComplete()) { - return; - } - if (PUBLIC_KEY_METHOD.equals(method)) { - if (!myHost.supportsPubkeyAuthentication()) { - continue; - } - if (!c.isAuthMethodAvailable(myHost.getUser(), PUBLIC_KEY_METHOD)) { - continue; - } - File key = myHost.getIdentityFile(); - if (key == null) { - for (String a : myHost.getHostKeyAlgorithms()) { - if (SSH_RSA_ALGORITHM.equals(a)) { - if (tryPublicKey(c, idRSAPath)) { - return; - } - } - else if (SSH_DSS_ALGORITHM.equals(a)) { - if (tryPublicKey(c, idDSAPath)) { - return; - } - } - } - } - else { - if (tryPublicKey(c, key.getPath())) { - return; - } - } - } - else if (KEYBOARD_INTERACTIVE_METHOD.equals(method)) { - if (!c.isAuthMethodAvailable(myHost.getUser(), KEYBOARD_INTERACTIVE_METHOD)) { - continue; - } - InteractiveSupport interactiveSupport = new InteractiveSupport(); - for (int i = myHost.getNumberOfPasswordPrompts(); i > 0; i--) { - if (c.isAuthenticationComplete()) { - return; - } - if (c.authenticateWithKeyboardInteractive(myHost.getUser(), interactiveSupport)) { - myLastError = ""; - myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), KEYBOARD_INTERACTIVE_METHOD, ""); - return; - } - else { - myLastError = SSHMainBundle.getString("sshmain.keyboard.interactive.failed"); - } - if (interactiveSupport.myPromptCount == 0 || interactiveSupport.myCancelled) { - // the interactive callback has never been asked or it was cancelled, exit the loop - myLastError = ""; - break; - } - } - } - else if (PASSWORD_METHOD.equals(method)) { - if (!myHost.supportsPasswordAuthentication()) { - continue; - } - if (!c.isAuthMethodAvailable(myHost.getUser(), PASSWORD_METHOD)) { - continue; - } - for (int i = 0; i < myHost.getNumberOfPasswordPrompts(); i++) { - String password = myXmlRpcClient.askPassword(myHandlerNo, getUserHostString(), i != 0, myLastError); - if (password == null) { - break; - } - else { - if (c.authenticateWithPassword(myHost.getUser(), password)) { - myLastError = ""; - myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), PASSWORD_METHOD, ""); - return; - } - else { - myLastError = SSHMainBundle.getString("sshmain.password.failed"); - } - } - } - } - } - myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), "", myLastError); - throw new IOException("Authentication failed: " + myLastError); - } - - /** - * @return user and host string - */ - private String getUserHostString() { - int port = myHost.getPort(); - return myHost.getUser() + "@" + myHost.getHostName() + (port == 22 ? "" : ":" + port); - } - - /** - * Try public key - * - * @param c a ssh connection - * @param keyPath a path to key - * @return true if authentication is successful - */ - private boolean tryPublicKey(final Connection c, final String keyPath) { - try { - final File file = new File(keyPath); - if (file.exists()) { - // if encrypted ask user for passphrase - String passphrase = null; - char[] text = FileUtil.loadFileText(file); - if (isEncryptedKey(text)) { - // need to ask passphrase from user - int i; - for (i = 0; i < myHost.getNumberOfPasswordPrompts(); i++) { - passphrase = myXmlRpcClient.askPassphrase(myHandlerNo, getUserHostString(), keyPath, i != 0, myLastError); - if (passphrase == null) { - // if no passphrase was entered, just return false and try something other - return false; - } - else { - try { - PEMDecoder.decode(text, passphrase); - myLastError = ""; - } - catch (IOException e) { - // decoding failed - myLastError = SSHMainBundle.message("sshmain.invalidpassphrase", keyPath); - continue; - } - break; - } - } - if (i == myHost.getNumberOfPasswordPrompts()) { - myLastError = SSHMainBundle.message("sshmain.too.mush.passphrase.guesses", keyPath, myHost.getNumberOfPasswordPrompts()); - return false; - } - } - // try authentication - if (c.authenticateWithPublicKey(myHost.getUser(), text, passphrase)) { - myLastError = ""; - myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), PUBLIC_KEY_METHOD, ""); - return true; - } - else { - if (passphrase != null) { - // mark as failed authentication only if passphrase were asked - myLastError = SSHMainBundle.message("sshmain.pk.authenitication.failed", keyPath); - } - else { - myLastError = ""; - } - } - } - return false; - } - catch (Exception e) { - return false; - } - } - - /** - * Check if the key is encrypted. The key is considered encrypted - * - * @param text the text of the key - * @return true if the key is encrypted - * @throws IOException if there is a problem with reading key - */ - private static boolean isEncryptedKey(char[] text) throws IOException { - BufferedReader in = new BufferedReader(new CharArrayReader(text)); - try { - String line; - while ((line = in.readLine()) != null) { - //noinspection HardCodedStringLiteral - if (line.startsWith("Proc-Type: ") && line.indexOf("ENCRYPTED") != -1) { - return true; - } - if (line.length() == 0) { - // empty line means end of the mime headers - break; - } - } - return false; - } - finally { - in.close(); - } - } - - /** - * Forward stream in separate thread. - * - * @param name the name of the stream - * @param out the output stream - * @param in the input stream - * @param releaseSemaphore if true the semaphore will be released - */ - private void forward(@NonNls final String name, final OutputStream out, final InputStream in, final boolean releaseSemaphore) { - final Runnable action = new Runnable() { - public void run() { - byte[] buffer = new byte[BUFFER_SIZE]; - int rc; - try { - try { - try { - while ((rc = in.read(buffer)) != -1) { - out.write(buffer, 0, rc); - } - } - finally { - out.close(); - } - } - finally { - in.close(); - } - } - catch (IOException e) { - System.err.println(SSHMainBundle.message("sshmain.forwarding.failed", name, e.getMessage())); - e.printStackTrace(); - myExitCode = 1; - if (releaseSemaphore) { - // in the case of error, release semaphore, so that application could exit - myForwardCompleted.release(1); - } - } - finally { - if (releaseSemaphore) { - myForwardCompleted.release(1); - } - } - } - }; - @SuppressWarnings({"HardCodedStringLiteral"}) final Thread t = new Thread(action, "Forwarding " + name); - t.setDaemon(true); - t.start(); - } - - - /** - * Configure known host database for connection - * - * @param c a connection - * @throws IOException if there is a IO problem - */ - private void configureKnownHosts(Connection c) throws IOException { - File knownHostFile = new File(knownHostPath); - if (knownHostFile.exists()) { - database.addHostkeys(knownHostFile); - } - final List algorithms = myHost.getHostKeyAlgorithms(); - c.setServerHostKeyAlgorithms(ArrayUtil.toStringArray(algorithms)); - } - - /** - * Parse command line arguments and create application instance. - * - * @param args command line arguments - * @return application instance - * @throws IOException if loading configuration file failed - */ - private static SSHMain parseArguments(String[] args) throws IOException { - if (args.length != 2 && args.length != 4) { - System.err.println(SSHMainBundle.message("sshmain.invalid.amount.of.arguments", Arrays.asList(args))); - System.exit(1); - } - int i = 0; - Integer port = null; - //noinspection HardCodedStringLiteral - if ("-p".equals(args[i])) { - i++; - port = Integer.parseInt(args[i++]); - } - String host = args[i++]; - String user; - int atIndex = host.indexOf('@'); - if (atIndex == -1) { - user = null; - } - else { - user = host.substring(0, atIndex); - host = host.substring(atIndex + 1); - } - String command = args[i]; - return new SSHMain(host, user, port, command); - } - - - /** - * Interactive callback support. The callback invokes Idea XML RPC server. - */ - class InteractiveSupport implements InteractiveCallback { - /** - * Prompt count - */ - int myPromptCount = 0; - /** - * true if keyboard interactive method was cancelled. - */ - boolean myCancelled; - - /** - * {@inheritDoc} - */ - @SuppressWarnings({"UseOfObsoleteCollectionType"}) - @Nullable - public String[] replyToChallenge(final String name, - final String instruction, - final int numPrompts, - final String[] prompt, - final boolean[] echo) throws Exception { - if (numPrompts == 0) { - return ArrayUtil.EMPTY_STRING_ARRAY; - } - myPromptCount++; - Vector vPrompts = new Vector(prompt.length); - Collections.addAll(vPrompts, prompt); - Vector vEcho = new Vector(prompt.length); - for (boolean e : echo) { - vEcho.add(e); - } - final Vector result = - myXmlRpcClient.replyToChallenge(myHandlerNo, getUserHostString(), name, instruction, numPrompts, vPrompts, vEcho, myLastError); - if (result == null) { - myCancelled = true; - String[] rc = new String[numPrompts]; - Arrays.fill(rc, ""); - return rc; - } - else { - return ArrayUtil.toStringArray(result); - } - } - } - - /** - * Server host key verifier that invokes Idea XML RPC server. - */ - private class HostKeyVerifier implements ServerHostKeyVerifier { - /** - * {@inheritDoc} - */ - public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { - try { - String s = System.getenv(GitSSHHandler.SSH_IGNORE_KNOWN_HOSTS_ENV); - if (s != null && Boolean.parseBoolean(s)) { - return true; - } - } - catch (Exception ex) { - // the known host check is not suppressed, proceed with normal check - } - try { - final int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); - final boolean isNew; - switch (result) { - case KnownHosts.HOSTKEY_IS_OK: - return true; - case KnownHosts.HOSTKEY_IS_NEW: - isNew = true; - break; - case KnownHosts.HOSTKEY_HAS_CHANGED: - isNew = false; - break; - default: - throw new IllegalStateException("Unknown verification result: " + result); - } - String fingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); - boolean keyCheck = myXmlRpcClient.verifyServerHostKey(myHandlerNo, hostname, port, serverHostKeyAlgorithm, fingerprint, isNew); - if (keyCheck) { - String hashedHostname = KnownHosts.createHashedHostname(hostname); - // Add the host key to the in-memory database - database.addHostkey(new String[]{hashedHostname}, serverHostKeyAlgorithm, serverHostKey); - // Also try to add the key to a known_host file - try { - KnownHosts.addHostkeyToFile(new File(knownHostPath), new String[]{hashedHostname}, serverHostKeyAlgorithm, serverHostKey); - } - catch (IOException ignore) { - // TODO log text - } - return true; - } - else { - System.err.println(SSHMainBundle.message("sshmain.invald.host.key", serverHostKeyAlgorithm, fingerprint)); - return false; - } - } - catch (Throwable t) { - System.err.println(SSHMainBundle.message("sshmain.failed.to.verify.key", t.getMessage())); - t.printStackTrace(); - return false; - } - } - } -} +/* + * Copyright 2000-2009 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.git4idea.ssh; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.ArrayUtil; +import com.trilead.ssh2.*; +import com.trilead.ssh2.crypto.PEMDecoder; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.util.*; +import java.util.concurrent.Semaphore; + +/** + * The main class for SSH client. It can only handle the following command line (which is use by GIT): + * git-ssh xmlRpcPort [-p port] host command. The program is wrapped in the script, so XML RPC port + * not settable using script interface. + *

+ * The code here is based on SwingShell example. + */ +@SuppressWarnings({"CallToPrintStackTrace", "UseOfSystemOutOrSystemErr"}) +public class SSHMain { + /** + * the semaphore + */ + private final Semaphore myForwardCompleted = new Semaphore(0); + /** + * the host + */ + private final SSHConfig.Host myHost; + /** + * Handler number + */ + private final int myHandlerNo; + /** + * the xml RPC port + */ + private final GitSSHXmlRpcClient myXmlRpcClient; + /** + * the command to run + */ + private final String myCommand; + /** + * the exit code + */ + private int myExitCode = 0; + /** + * The last error + */ + private String myLastError = ""; + /** + * Path to known hosts file + */ + @NonNls private static final String knownHostPath = SSHConfig.USER_HOME + "/.ssh/known_hosts"; + /** + * Path to DSA key + */ + @NonNls private static final String idDSAPath = SSHConfig.USER_HOME + "/.ssh/id_dsa"; + /** + * Path to RSA key + */ + @NonNls private static final String idRSAPath = SSHConfig.USER_HOME + "/.ssh/id_rsa"; + + /** + * database of known hosts + */ + private final KnownHosts database = new KnownHosts(); + /** + * size of the buffers for stream forwarding + */ + private static final int BUFFER_SIZE = 16 * 1024; + /** + * public key authentication method + */ + @NonNls public static final String PUBLIC_KEY_METHOD = "publickey"; + /** + * keyboard interactive method + */ + @NonNls public static final String KEYBOARD_INTERACTIVE_METHOD = "keyboard-interactive"; + /** + * password method + */ + @NonNls public static final String PASSWORD_METHOD = "password"; + /** + * RSA algorithm + */ + @NonNls public static final String SSH_RSA_ALGORITHM = "ssh-rsa"; + /** + * DSS algorithm + */ + @NonNls public static final String SSH_DSS_ALGORITHM = "ssh-dss"; + + + /** + * A constructor + * + * @param host a host + * @param username a name of user (from URL) + * @param port a port + * @param command a command + * @throws IOException if config file could not be loaded + */ + private SSHMain(String host, String username, Integer port, String command) throws IOException { + SSHConfig config = SSHConfig.load(); + myHost = config.lookup(username, host, port); + myHandlerNo = Integer.parseInt(System.getenv(GitSSHHandler.SSH_HANDLER_ENV)); + int xmlRpcPort = Integer.parseInt(System.getenv(GitSSHHandler.SSH_PORT_ENV)); + myXmlRpcClient = new GitSSHXmlRpcClient(xmlRpcPort, myHost.isBatchMode()); + myCommand = command; + } + + /** + * The application entry point + * + * @param args program arguments + */ + public static void main(String[] args) { + try { + SSHMain app = parseArguments(args); + app.start(); + System.exit(app.myExitCode); + } + catch (Throwable t) { + t.printStackTrace(); + System.exit(1); + } + } + + /** + * Start the application + * + * @throws IOException if there is a problem with connection + * @throws InterruptedException if thread was interrupted + */ + private void start() throws IOException, InterruptedException { + Connection c = new Connection(myHost.getHostName(), myHost.getPort()); + try { + configureKnownHosts(c); + c.connect(new HostKeyVerifier()); + authenticate(c); + final Session s = c.openSession(); + try { + s.execCommand(myCommand); + // Note that stdin is not being waited using semaphore. + // Instead, the SSH process waits for remote process exit + // if remote process exited, none is interested in stdin + // anyway. + forward("stdin", s.getStdin(), System.in, false); + forward("stdout", System.out, s.getStdout(), true); + forward("stderr", System.err, s.getStderr(), true); + myForwardCompleted.acquire(2); // wait only for stderr and stdout + s.waitForCondition(ChannelCondition.EXIT_STATUS, Long.MAX_VALUE); + Integer exitStatus = s.getExitStatus(); + if (exitStatus == null) { + // broken exit status + exitStatus = 1; + } + System.exit(exitStatus.intValue() == 0 ? myExitCode : exitStatus.intValue()); + } + finally { + s.close(); + } + } + finally { + c.close(); + } + } + + /** + * Authenticate using some supported methods. If authentication fails, + * the method throws {@link IOException}. + * + * @param c the connection to use for authentication + * @throws IOException in case of IO error or authentication failure + */ + private void authenticate(final Connection c) throws IOException { + LinkedList methods = new LinkedList(myHost.getPreferredMethods()); + String lastSuccessfulMethod = myXmlRpcClient.getLastSuccessful(myHandlerNo, getUserHostString()); + if (lastSuccessfulMethod != null && lastSuccessfulMethod.length() > 0 && methods.remove(lastSuccessfulMethod)) { + methods.addFirst(lastSuccessfulMethod); + } + for (String method : methods) { + if (c.isAuthenticationComplete()) { + return; + } + if (PUBLIC_KEY_METHOD.equals(method)) { + if (!myHost.supportsPubkeyAuthentication()) { + continue; + } + if (!c.isAuthMethodAvailable(myHost.getUser(), PUBLIC_KEY_METHOD)) { + continue; + } + File key = myHost.getIdentityFile(); + if (key == null) { + for (String a : myHost.getHostKeyAlgorithms()) { + if (SSH_RSA_ALGORITHM.equals(a)) { + if (tryPublicKey(c, idRSAPath)) { + return; + } + } + else if (SSH_DSS_ALGORITHM.equals(a)) { + if (tryPublicKey(c, idDSAPath)) { + return; + } + } + } + } + else { + if (tryPublicKey(c, key.getPath())) { + return; + } + } + } + else if (KEYBOARD_INTERACTIVE_METHOD.equals(method)) { + if (!c.isAuthMethodAvailable(myHost.getUser(), KEYBOARD_INTERACTIVE_METHOD)) { + continue; + } + InteractiveSupport interactiveSupport = new InteractiveSupport(); + for (int i = myHost.getNumberOfPasswordPrompts(); i > 0; i--) { + if (c.isAuthenticationComplete()) { + return; + } + if (c.authenticateWithKeyboardInteractive(myHost.getUser(), interactiveSupport)) { + myLastError = ""; + myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), KEYBOARD_INTERACTIVE_METHOD, ""); + return; + } + else { + myLastError = SSHMainBundle.getString("sshmain.keyboard.interactive.failed"); + } + if (interactiveSupport.myPromptCount == 0 || interactiveSupport.myCancelled) { + // the interactive callback has never been asked or it was cancelled, exit the loop + myLastError = ""; + break; + } + } + } + else if (PASSWORD_METHOD.equals(method)) { + if (!myHost.supportsPasswordAuthentication()) { + continue; + } + if (!c.isAuthMethodAvailable(myHost.getUser(), PASSWORD_METHOD)) { + continue; + } + for (int i = 0; i < myHost.getNumberOfPasswordPrompts(); i++) { + String password = myXmlRpcClient.askPassword(myHandlerNo, getUserHostString(), i != 0, myLastError); + if (password == null) { + break; + } + else { + if (c.authenticateWithPassword(myHost.getUser(), password)) { + myLastError = ""; + myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), PASSWORD_METHOD, ""); + return; + } + else { + myLastError = SSHMainBundle.getString("sshmain.password.failed"); + } + } + } + } + } + myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), "", myLastError); + throw new IOException("Authentication failed: " + myLastError); + } + + /** + * @return user and host string + */ + private String getUserHostString() { + int port = myHost.getPort(); + return myHost.getUser() + "@" + myHost.getHostName() + (port == 22 ? "" : ":" + port); + } + + /** + * Try public key + * + * @param c a ssh connection + * @param keyPath a path to key + * @return true if authentication is successful + */ + private boolean tryPublicKey(final Connection c, final String keyPath) { + try { + final File file = new File(keyPath); + if (file.exists()) { + // if encrypted ask user for passphrase + String passphrase = null; + char[] text = FileUtil.loadFileText(file); + if (isEncryptedKey(text)) { + // need to ask passphrase from user + int i; + for (i = 0; i < myHost.getNumberOfPasswordPrompts(); i++) { + passphrase = myXmlRpcClient.askPassphrase(myHandlerNo, getUserHostString(), keyPath, i != 0, myLastError); + if (passphrase == null) { + // if no passphrase was entered, just return false and try something other + return false; + } + else { + try { + PEMDecoder.decode(text, passphrase); + myLastError = ""; + } + catch (IOException e) { + // decoding failed + myLastError = SSHMainBundle.message("sshmain.invalidpassphrase", keyPath); + continue; + } + break; + } + } + if (i == myHost.getNumberOfPasswordPrompts()) { + myLastError = SSHMainBundle.message("sshmain.too.mush.passphrase.guesses", keyPath, myHost.getNumberOfPasswordPrompts()); + return false; + } + } + // try authentication + if (c.authenticateWithPublicKey(myHost.getUser(), text, passphrase)) { + myLastError = ""; + myXmlRpcClient.setLastSuccessful(myHandlerNo, getUserHostString(), PUBLIC_KEY_METHOD, ""); + return true; + } + else { + if (passphrase != null) { + // mark as failed authentication only if passphrase were asked + myLastError = SSHMainBundle.message("sshmain.pk.authenitication.failed", keyPath); + } + else { + myLastError = ""; + } + } + } + return false; + } + catch (Exception e) { + return false; + } + } + + /** + * Check if the key is encrypted. The key is considered encrypted + * + * @param text the text of the key + * @return true if the key is encrypted + * @throws IOException if there is a problem with reading key + */ + private static boolean isEncryptedKey(char[] text) throws IOException { + BufferedReader in = new BufferedReader(new CharArrayReader(text)); + try { + String line; + while ((line = in.readLine()) != null) { + //noinspection HardCodedStringLiteral + if (line.startsWith("Proc-Type: ") && line.indexOf("ENCRYPTED") != -1) { + return true; + } + if (line.length() == 0) { + // empty line means end of the mime headers + break; + } + } + return false; + } + finally { + in.close(); + } + } + + /** + * Forward stream in separate thread. + * + * @param name the name of the stream + * @param out the output stream + * @param in the input stream + * @param releaseSemaphore if true the semaphore will be released + */ + private void forward(@NonNls final String name, final OutputStream out, final InputStream in, final boolean releaseSemaphore) { + final Runnable action = new Runnable() { + public void run() { + byte[] buffer = new byte[BUFFER_SIZE]; + int rc; + try { + try { + try { + while ((rc = in.read(buffer)) != -1) { + out.write(buffer, 0, rc); + } + } + finally { + out.close(); + } + } + finally { + in.close(); + } + } + catch (IOException e) { + System.err.println(SSHMainBundle.message("sshmain.forwarding.failed", name, e.getMessage())); + e.printStackTrace(); + myExitCode = 1; + if (releaseSemaphore) { + // in the case of error, release semaphore, so that application could exit + myForwardCompleted.release(1); + } + } + finally { + if (releaseSemaphore) { + myForwardCompleted.release(1); + } + } + } + }; + @SuppressWarnings({"HardCodedStringLiteral"}) final Thread t = new Thread(action, "Forwarding " + name); + t.setDaemon(true); + t.start(); + } + + + /** + * Configure known host database for connection + * + * @param c a connection + * @throws IOException if there is a IO problem + */ + private void configureKnownHosts(Connection c) throws IOException { + File knownHostFile = new File(knownHostPath); + if (knownHostFile.exists()) { + database.addHostkeys(knownHostFile); + } + final List algorithms = myHost.getHostKeyAlgorithms(); + c.setServerHostKeyAlgorithms(ArrayUtil.toStringArray(algorithms)); + } + + /** + * Parse command line arguments and create application instance. + * + * @param args command line arguments + * @return application instance + * @throws IOException if loading configuration file failed + */ + private static SSHMain parseArguments(String[] args) throws IOException { + if (args.length != 2 && args.length != 4) { + System.err.println(SSHMainBundle.message("sshmain.invalid.amount.of.arguments", Arrays.asList(args))); + System.exit(1); + } + int i = 0; + Integer port = null; + //noinspection HardCodedStringLiteral + if ("-p".equals(args[i])) { + i++; + port = Integer.parseInt(args[i++]); + } + String host = args[i++]; + String user; + int atIndex = host.indexOf('@'); + if (atIndex == -1) { + user = null; + } + else { + user = host.substring(0, atIndex); + host = host.substring(atIndex + 1); + } + String command = args[i]; + return new SSHMain(host, user, port, command); + } + + + /** + * Interactive callback support. The callback invokes Idea XML RPC server. + */ + class InteractiveSupport implements InteractiveCallback { + /** + * Prompt count + */ + int myPromptCount = 0; + /** + * true if keyboard interactive method was cancelled. + */ + boolean myCancelled; + + /** + * {@inheritDoc} + */ + @SuppressWarnings({"UseOfObsoleteCollectionType"}) + @Nullable + public String[] replyToChallenge(final String name, + final String instruction, + final int numPrompts, + final String[] prompt, + final boolean[] echo) throws Exception { + if (numPrompts == 0) { + return ArrayUtil.EMPTY_STRING_ARRAY; + } + myPromptCount++; + Vector vPrompts = new Vector(prompt.length); + Collections.addAll(vPrompts, prompt); + Vector vEcho = new Vector(prompt.length); + for (boolean e : echo) { + vEcho.add(e); + } + final Vector result = + myXmlRpcClient.replyToChallenge(myHandlerNo, getUserHostString(), name, instruction, numPrompts, vPrompts, vEcho, myLastError); + if (result == null) { + myCancelled = true; + String[] rc = new String[numPrompts]; + Arrays.fill(rc, ""); + return rc; + } + else { + return ArrayUtil.toStringArray(result); + } + } + } + + /** + * Server host key verifier that invokes Idea XML RPC server. + */ + private class HostKeyVerifier implements ServerHostKeyVerifier { + /** + * {@inheritDoc} + */ + public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { + try { + String s = System.getenv(GitSSHHandler.SSH_IGNORE_KNOWN_HOSTS_ENV); + if (s != null && Boolean.parseBoolean(s)) { + return true; + } + } + catch (Exception ex) { + // the known host check is not suppressed, proceed with normal check + } + try { + final int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); + final boolean isNew; + switch (result) { + case KnownHosts.HOSTKEY_IS_OK: + return true; + case KnownHosts.HOSTKEY_IS_NEW: + isNew = true; + break; + case KnownHosts.HOSTKEY_HAS_CHANGED: + isNew = false; + break; + default: + throw new IllegalStateException("Unknown verification result: " + result); + } + String fingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); + boolean keyCheck = myXmlRpcClient.verifyServerHostKey(myHandlerNo, hostname, port, serverHostKeyAlgorithm, fingerprint, isNew); + if (keyCheck) { + String hashedHostname = KnownHosts.createHashedHostname(hostname); + // Add the host key to the in-memory database + database.addHostkey(new String[]{hashedHostname}, serverHostKeyAlgorithm, serverHostKey); + // Also try to add the key to a known_host file + try { + KnownHosts.addHostkeyToFile(new File(knownHostPath), new String[]{hashedHostname}, serverHostKeyAlgorithm, serverHostKey); + } + catch (IOException ignore) { + // TODO log text + } + return true; + } + else { + System.err.println(SSHMainBundle.message("sshmain.invald.host.key", serverHostKeyAlgorithm, fingerprint)); + return false; + } + } + catch (Throwable t) { + System.err.println(SSHMainBundle.message("sshmain.failed.to.verify.key", t.getMessage())); + t.printStackTrace(); + return false; + } + } + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java index db8badf432da..905a6f6248a8 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java @@ -1,411 +1,411 @@ -// Copyright 2008-2010 Victor Iacoban -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software distributed under -// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions and -// limitations under the License. -package org.zmlx.hg4idea; - -import com.intellij.concurrency.JobScheduler; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.fileEditor.FileEditorManagerAdapter; -import com.intellij.openapi.fileEditor.FileEditorManagerEvent; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.options.Configurable; -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.vcs.AbstractVcs; -import com.intellij.openapi.vcs.CommittedChangesProvider; -import com.intellij.openapi.vcs.ProjectLevelVcsManager; -import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.annotate.AnnotationProvider; -import com.intellij.openapi.vcs.changes.ChangeProvider; -import com.intellij.openapi.vcs.checkin.CheckinEnvironment; -import com.intellij.openapi.vcs.diff.DiffProvider; -import com.intellij.openapi.vcs.history.VcsHistoryProvider; -import com.intellij.openapi.vcs.merge.MergeProvider; -import com.intellij.openapi.vcs.rollback.RollbackEnvironment; -import com.intellij.openapi.vcs.update.UpdateEnvironment; -import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileListener; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.openapi.wm.StatusBar; -import com.intellij.openapi.wm.WindowManager; -import com.intellij.util.containers.ComparatorDelegate; -import com.intellij.util.containers.Convertor; -import com.intellij.util.messages.MessageBusConnection; -import com.intellij.util.messages.Topic; -import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.provider.*; -import org.zmlx.hg4idea.provider.annotate.HgAnnotationProvider; -import org.zmlx.hg4idea.provider.commit.HgCheckinEnvironment; -import org.zmlx.hg4idea.provider.update.HgIntegrateEnvironment; -import org.zmlx.hg4idea.provider.update.HgUpdateEnvironment; -import org.zmlx.hg4idea.ui.HgChangesetStatus; -import org.zmlx.hg4idea.ui.HgCurrentBranchStatus; - -import javax.swing.*; -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -public class HgVcs extends AbstractVcs { - - public static final Topic BRANCH_TOPIC = - new Topic("hg4idea.branch", HgUpdater.class); - - public static final Topic INCOMING_TOPIC = - new Topic("hg4idea.incoming", HgUpdater.class); - - public static final Topic OUTGOING_TOPIC = - new Topic("hg4idea.outgoing", HgUpdater.class); - - public static final Icon MERCURIAL_ICON = IconLoader.getIcon("/images/mercurial.png"); - - private static final Icon INCOMING_ICON = IconLoader.getIcon("/actions/moveDown.png"); - private static final Icon OUTGOING_ICON = IconLoader.getIcon("/actions/moveUp.png"); - - public static final String VCS_NAME = "hg4idea"; - public static final String DIRSTATE_FILE_PATH = ".hg/dirstate"; - public static final String NOTIFICATION_GROUP_ID = "Mercurial"; - public static final String HG_EXECUTABLE_FILE_NAME = (SystemInfo.isWindows ? "hg.exe" : "hg"); - - private static final String ORIG_FILE_PATTERN = "*.orig"; - - private final HgChangeProvider changeProvider; - private final HgRollbackEnvironment rollbackEnvironment; - private final HgDiffProvider diffProvider; - private final HgHistoryProvider historyProvider; - private final HgCheckinEnvironment checkinEnvironment; - private final HgAnnotationProvider annotationProvider; - private final HgUpdateEnvironment updateEnvironment; - private final HgIntegrateEnvironment integrateEnvironment; - private final HgCachingCommitedChangesProvider commitedChangesProvider; - private final HgCurrentBranchStatus hgCurrentBranchStatus = new HgCurrentBranchStatus(); - private final HgChangesetStatus incomingChangesStatus = new HgChangesetStatus(INCOMING_ICON); - private final HgChangesetStatus outgoingChangesStatus = new HgChangesetStatus(OUTGOING_ICON); - private MessageBusConnection messageBusConnection; - private ScheduledFuture changesUpdaterScheduledFuture; - private final HgGlobalSettings globalSettings; - private final HgProjectSettings projectSettings; - private final ProjectLevelVcsManager myVcsManager; - - private boolean started = false; - private HgVFSListener myVFSListener; - private VirtualFileListener myDirStateChangeListener; - private final HgMergeProvider myMergeProvider; - - public HgVcs(Project project, - HgGlobalSettings globalSettings, HgProjectSettings projectSettings, - ProjectLevelVcsManager vcsManager) { - super(project, VCS_NAME); - this.globalSettings = globalSettings; - this.projectSettings = projectSettings; - myVcsManager = vcsManager; - changeProvider = new HgChangeProvider(project, getKeyInstanceMethod()); - rollbackEnvironment = new HgRollbackEnvironment(project); - diffProvider = new HgDiffProvider(project); - historyProvider = new HgHistoryProvider(project); - checkinEnvironment = new HgCheckinEnvironment(project); - annotationProvider = new HgAnnotationProvider(project); - updateEnvironment = new HgUpdateEnvironment(project); - integrateEnvironment = new HgIntegrateEnvironment(project); - commitedChangesProvider = new HgCachingCommitedChangesProvider(project); - myDirStateChangeListener = new HgDirStateChangeListener(myProject); - myMergeProvider = new HgMergeProvider(myProject); - } - - public String getDisplayName() { - return HgVcsMessages.message("hg4idea.mercurial"); - } - - public Configurable getConfigurable() { - return new HgProjectConfigurable(projectSettings); - } - - @Override - public ChangeProvider getChangeProvider() { - if (!started) { - return null; - } - - return changeProvider; - } - - @Override - public RollbackEnvironment getRollbackEnvironment() { - if (!started) { - return null; - } - - return rollbackEnvironment; - } - - @Override - public DiffProvider getDiffProvider() { - if (!started) { - return null; - } - - return diffProvider; - } - - @Override - public VcsHistoryProvider getVcsHistoryProvider() { - if (!started) { - return null; - } - - return historyProvider; - } - - @Override - public VcsHistoryProvider getVcsBlockHistoryProvider() { - return getVcsHistoryProvider(); - } - - @Override - public CheckinEnvironment getCheckinEnvironment() { - if (!started) { - return null; - } - - return checkinEnvironment; - } - - @Override - public AnnotationProvider getAnnotationProvider() { - if (!started) { - return null; - } - - return annotationProvider; - } - - @Override - public MergeProvider getMergeProvider() { - return myMergeProvider; - } - - @Override - public UpdateEnvironment getUpdateEnvironment() { - if (!started) { - return null; - } - - return updateEnvironment; - } - - @Override - public UpdateEnvironment getIntegrateEnvironment() { - if (!started) { - return null; - } - - return integrateEnvironment; - } - - @Override - public CommittedChangesProvider getCommittedChangesProvider() { - if (!started) { - return null; - } - return null; -// return commitedChangesProvider; - } - - @Override - public boolean allowsNestedRoots() { - return true; - } - - @Override - public List filterUniqueRoots(final List in, final Convertor convertor) { - Collections.sort(in, new ComparatorDelegate(convertor, FilePathComparator.getInstance())); - - for (int i = 1; i < in.size(); i++) { - final S sChild = in.get(i); - final VirtualFile child = convertor.convert(sChild); - final VirtualFile childRoot = HgUtil.getHgRootOrNull(myProject, child); - if (childRoot == null) { - continue; - } - for (int j = i - 1; j >= 0; --j) { - final S sParent = in.get(j); - final VirtualFile parent = convertor.convert(sParent); - // if the parent is an ancestor of the child and that they share common root, the child is removed - if (VfsUtil.isAncestor(parent, child, false) && VfsUtil.isAncestor(childRoot, parent, false)) { - in.remove(i); - //noinspection AssignmentToForLoopParameter - --i; - break; - } - } - } - return in; - } - - - @Override - public RootsConvertor getCustomConvertor() { - return HgRootsHandler.getInstance(myProject); - } - - @Override - public boolean isVersionedDirectory(VirtualFile dir) { - return HgUtil.getNearestHgRoot(dir) != null; - } - - public boolean isStarted() { - return started; - } - - @Override - protected void shutdown() throws VcsException { - started = false; - } - - @Override - public void activate() { - // validate hg executable - if (ApplicationManager.getApplication().isUnitTestMode()) { - started = true; - } else { - HgExecutableValidator validator = new HgExecutableValidator(myProject); - started = validator.check(globalSettings); - } - if (!started) { - return; - } - - // status bar - StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); - if (statusBar != null) { - statusBar.addWidget(hgCurrentBranchStatus, myProject); - statusBar.addWidget(incomingChangesStatus, myProject); - statusBar.addWidget(outgoingChangesStatus, myProject); - } - - // updaters and listeners - final HgIncomingStatusUpdater incomingUpdater = new HgIncomingStatusUpdater(incomingChangesStatus, projectSettings); - final HgOutgoingStatusUpdater outgoingUpdater = new HgOutgoingStatusUpdater(outgoingChangesStatus, projectSettings); - changesUpdaterScheduledFuture = JobScheduler.getScheduler().scheduleWithFixedDelay( - new Runnable() { - public void run() { - incomingUpdater.update(myProject); - outgoingUpdater.update(myProject); - } - }, 0, HgGlobalSettings.getIncomingCheckIntervalSeconds(), TimeUnit.SECONDS); - - messageBusConnection = myProject.getMessageBus().connect(); - messageBusConnection.subscribe(INCOMING_TOPIC, incomingUpdater); - messageBusConnection.subscribe(OUTGOING_TOPIC, outgoingUpdater); - messageBusConnection.subscribe(BRANCH_TOPIC, new HgCurrentBranchStatusUpdater(hgCurrentBranchStatus)); - messageBusConnection.subscribe( - FileEditorManagerListener.FILE_EDITOR_MANAGER, - new FileEditorManagerAdapter() { - @Override - public void selectionChanged(FileEditorManagerEvent event) { - Project project = event.getManager().getProject(); - project.getMessageBus() - .asyncPublisher(BRANCH_TOPIC) - .update(project); - } - } - ); - - myVFSListener = new HgVFSListener(myProject, this); - VirtualFileManager.getInstance().addVirtualFileListener(myDirStateChangeListener); - - // ignore temporary files - final String ignoredPattern = FileTypeManager.getInstance().getIgnoredFilesList(); - if (!ignoredPattern.contains(ORIG_FILE_PATTERN)) { - final String newPattern = ignoredPattern + (ignoredPattern.endsWith(";") ? "" : ";") + ORIG_FILE_PATTERN; - HgUtil.runWriteActionLater(new Runnable() { - public void run() { - FileTypeManager.getInstance().setIgnoredFilesList(newPattern); - } - }); - } - } - - @Override - public void deactivate() { - if (!started) { - return; - } - - StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); - if (messageBusConnection != null) { - messageBusConnection.disconnect(); - } - if (changesUpdaterScheduledFuture != null) { - changesUpdaterScheduledFuture.cancel(true); - } - if (statusBar != null) { - //statusBar.removeCustomIndicationComponent(incomingChangesStatus); - //statusBar.removeCustomIndicationComponent(outgoingChangesStatus); - //statusBar.removeCustomIndicationComponent(hgCurrentBranchStatus); - } - - if (myVFSListener != null) { - Disposer.dispose(myVFSListener); - myVFSListener = null; - } - - VirtualFileManager.getInstance().removeVirtualFileListener(myDirStateChangeListener); - } - - @Nullable - public static HgVcs getInstance(Project project) { - if (project == null || project.isDisposed()) { return null; } - final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project); - if (vcsManager == null) { return null; } - return (HgVcs) vcsManager.findVcsByName(VCS_NAME); - } - - private static String ourTestHgExecutablePath; // path to hg in test mode - - /** - * Sets the path to hg executable used in the test mode. - */ - public static void setTestHgExecutablePath(String path) { - ourTestHgExecutablePath = path; - } - - /** - * Returns the hg executable file. - * If it is a test, returns the special value set in the test setup. - * If it is a normal app, returns the value from global settings. - */ - public String getHgExecutable() { - if (ApplicationManager.getApplication().isUnitTestMode()) { - return (new File(ourTestHgExecutablePath, HG_EXECUTABLE_FILE_NAME)).getPath(); - } - return globalSettings.getHgExecutable(); - } - - public HgGlobalSettings getGlobalSettings() { - return globalSettings; - } - - public void showMessageInConsole(String message, final TextAttributes style) { - myVcsManager.addMessageToConsoleWindow(message, style); - } - -} +// Copyright 2008-2010 Victor Iacoban +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions and +// limitations under the License. +package org.zmlx.hg4idea; + +import com.intellij.concurrency.JobScheduler; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileEditor.FileEditorManagerAdapter; +import com.intellij.openapi.fileEditor.FileEditorManagerEvent; +import com.intellij.openapi.fileEditor.FileEditorManagerListener; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.options.Configurable; +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.vcs.AbstractVcs; +import com.intellij.openapi.vcs.CommittedChangesProvider; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.annotate.AnnotationProvider; +import com.intellij.openapi.vcs.changes.ChangeProvider; +import com.intellij.openapi.vcs.checkin.CheckinEnvironment; +import com.intellij.openapi.vcs.diff.DiffProvider; +import com.intellij.openapi.vcs.history.VcsHistoryProvider; +import com.intellij.openapi.vcs.merge.MergeProvider; +import com.intellij.openapi.vcs.rollback.RollbackEnvironment; +import com.intellij.openapi.vcs.update.UpdateEnvironment; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileListener; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.util.containers.ComparatorDelegate; +import com.intellij.util.containers.Convertor; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.messages.Topic; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.provider.*; +import org.zmlx.hg4idea.provider.annotate.HgAnnotationProvider; +import org.zmlx.hg4idea.provider.commit.HgCheckinEnvironment; +import org.zmlx.hg4idea.provider.update.HgIntegrateEnvironment; +import org.zmlx.hg4idea.provider.update.HgUpdateEnvironment; +import org.zmlx.hg4idea.ui.HgChangesetStatus; +import org.zmlx.hg4idea.ui.HgCurrentBranchStatus; + +import javax.swing.*; +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class HgVcs extends AbstractVcs { + + public static final Topic BRANCH_TOPIC = + new Topic("hg4idea.branch", HgUpdater.class); + + public static final Topic INCOMING_TOPIC = + new Topic("hg4idea.incoming", HgUpdater.class); + + public static final Topic OUTGOING_TOPIC = + new Topic("hg4idea.outgoing", HgUpdater.class); + + public static final Icon MERCURIAL_ICON = IconLoader.getIcon("/images/mercurial.png"); + + private static final Icon INCOMING_ICON = IconLoader.getIcon("/actions/moveDown.png"); + private static final Icon OUTGOING_ICON = IconLoader.getIcon("/actions/moveUp.png"); + + public static final String VCS_NAME = "hg4idea"; + public static final String DIRSTATE_FILE_PATH = ".hg/dirstate"; + public static final String NOTIFICATION_GROUP_ID = "Mercurial"; + public static final String HG_EXECUTABLE_FILE_NAME = (SystemInfo.isWindows ? "hg.exe" : "hg"); + + private static final String ORIG_FILE_PATTERN = "*.orig"; + + private final HgChangeProvider changeProvider; + private final HgRollbackEnvironment rollbackEnvironment; + private final HgDiffProvider diffProvider; + private final HgHistoryProvider historyProvider; + private final HgCheckinEnvironment checkinEnvironment; + private final HgAnnotationProvider annotationProvider; + private final HgUpdateEnvironment updateEnvironment; + private final HgIntegrateEnvironment integrateEnvironment; + private final HgCachingCommitedChangesProvider commitedChangesProvider; + private final HgCurrentBranchStatus hgCurrentBranchStatus = new HgCurrentBranchStatus(); + private final HgChangesetStatus incomingChangesStatus = new HgChangesetStatus(INCOMING_ICON); + private final HgChangesetStatus outgoingChangesStatus = new HgChangesetStatus(OUTGOING_ICON); + private MessageBusConnection messageBusConnection; + private ScheduledFuture changesUpdaterScheduledFuture; + private final HgGlobalSettings globalSettings; + private final HgProjectSettings projectSettings; + private final ProjectLevelVcsManager myVcsManager; + + private boolean started = false; + private HgVFSListener myVFSListener; + private VirtualFileListener myDirStateChangeListener; + private final HgMergeProvider myMergeProvider; + + public HgVcs(Project project, + HgGlobalSettings globalSettings, HgProjectSettings projectSettings, + ProjectLevelVcsManager vcsManager) { + super(project, VCS_NAME); + this.globalSettings = globalSettings; + this.projectSettings = projectSettings; + myVcsManager = vcsManager; + changeProvider = new HgChangeProvider(project, getKeyInstanceMethod()); + rollbackEnvironment = new HgRollbackEnvironment(project); + diffProvider = new HgDiffProvider(project); + historyProvider = new HgHistoryProvider(project); + checkinEnvironment = new HgCheckinEnvironment(project); + annotationProvider = new HgAnnotationProvider(project); + updateEnvironment = new HgUpdateEnvironment(project); + integrateEnvironment = new HgIntegrateEnvironment(project); + commitedChangesProvider = new HgCachingCommitedChangesProvider(project); + myDirStateChangeListener = new HgDirStateChangeListener(myProject); + myMergeProvider = new HgMergeProvider(myProject); + } + + public String getDisplayName() { + return HgVcsMessages.message("hg4idea.mercurial"); + } + + public Configurable getConfigurable() { + return new HgProjectConfigurable(projectSettings); + } + + @Override + public ChangeProvider getChangeProvider() { + if (!started) { + return null; + } + + return changeProvider; + } + + @Override + public RollbackEnvironment getRollbackEnvironment() { + if (!started) { + return null; + } + + return rollbackEnvironment; + } + + @Override + public DiffProvider getDiffProvider() { + if (!started) { + return null; + } + + return diffProvider; + } + + @Override + public VcsHistoryProvider getVcsHistoryProvider() { + if (!started) { + return null; + } + + return historyProvider; + } + + @Override + public VcsHistoryProvider getVcsBlockHistoryProvider() { + return getVcsHistoryProvider(); + } + + @Override + public CheckinEnvironment getCheckinEnvironment() { + if (!started) { + return null; + } + + return checkinEnvironment; + } + + @Override + public AnnotationProvider getAnnotationProvider() { + if (!started) { + return null; + } + + return annotationProvider; + } + + @Override + public MergeProvider getMergeProvider() { + return myMergeProvider; + } + + @Override + public UpdateEnvironment getUpdateEnvironment() { + if (!started) { + return null; + } + + return updateEnvironment; + } + + @Override + public UpdateEnvironment getIntegrateEnvironment() { + if (!started) { + return null; + } + + return integrateEnvironment; + } + + @Override + public CommittedChangesProvider getCommittedChangesProvider() { + if (!started) { + return null; + } + return null; +// return commitedChangesProvider; + } + + @Override + public boolean allowsNestedRoots() { + return true; + } + + @Override + public List filterUniqueRoots(final List in, final Convertor convertor) { + Collections.sort(in, new ComparatorDelegate(convertor, FilePathComparator.getInstance())); + + for (int i = 1; i < in.size(); i++) { + final S sChild = in.get(i); + final VirtualFile child = convertor.convert(sChild); + final VirtualFile childRoot = HgUtil.getHgRootOrNull(myProject, child); + if (childRoot == null) { + continue; + } + for (int j = i - 1; j >= 0; --j) { + final S sParent = in.get(j); + final VirtualFile parent = convertor.convert(sParent); + // if the parent is an ancestor of the child and that they share common root, the child is removed + if (VfsUtil.isAncestor(parent, child, false) && VfsUtil.isAncestor(childRoot, parent, false)) { + in.remove(i); + //noinspection AssignmentToForLoopParameter + --i; + break; + } + } + } + return in; + } + + + @Override + public RootsConvertor getCustomConvertor() { + return HgRootsHandler.getInstance(myProject); + } + + @Override + public boolean isVersionedDirectory(VirtualFile dir) { + return HgUtil.getNearestHgRoot(dir) != null; + } + + public boolean isStarted() { + return started; + } + + @Override + protected void shutdown() throws VcsException { + started = false; + } + + @Override + public void activate() { + // validate hg executable + if (ApplicationManager.getApplication().isUnitTestMode()) { + started = true; + } else { + HgExecutableValidator validator = new HgExecutableValidator(myProject); + started = validator.check(globalSettings); + } + if (!started) { + return; + } + + // status bar + StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); + if (statusBar != null) { + statusBar.addWidget(hgCurrentBranchStatus, myProject); + statusBar.addWidget(incomingChangesStatus, myProject); + statusBar.addWidget(outgoingChangesStatus, myProject); + } + + // updaters and listeners + final HgIncomingStatusUpdater incomingUpdater = new HgIncomingStatusUpdater(incomingChangesStatus, projectSettings); + final HgOutgoingStatusUpdater outgoingUpdater = new HgOutgoingStatusUpdater(outgoingChangesStatus, projectSettings); + changesUpdaterScheduledFuture = JobScheduler.getScheduler().scheduleWithFixedDelay( + new Runnable() { + public void run() { + incomingUpdater.update(myProject); + outgoingUpdater.update(myProject); + } + }, 0, HgGlobalSettings.getIncomingCheckIntervalSeconds(), TimeUnit.SECONDS); + + messageBusConnection = myProject.getMessageBus().connect(); + messageBusConnection.subscribe(INCOMING_TOPIC, incomingUpdater); + messageBusConnection.subscribe(OUTGOING_TOPIC, outgoingUpdater); + messageBusConnection.subscribe(BRANCH_TOPIC, new HgCurrentBranchStatusUpdater(hgCurrentBranchStatus)); + messageBusConnection.subscribe( + FileEditorManagerListener.FILE_EDITOR_MANAGER, + new FileEditorManagerAdapter() { + @Override + public void selectionChanged(FileEditorManagerEvent event) { + Project project = event.getManager().getProject(); + project.getMessageBus() + .asyncPublisher(BRANCH_TOPIC) + .update(project); + } + } + ); + + myVFSListener = new HgVFSListener(myProject, this); + VirtualFileManager.getInstance().addVirtualFileListener(myDirStateChangeListener); + + // ignore temporary files + final String ignoredPattern = FileTypeManager.getInstance().getIgnoredFilesList(); + if (!ignoredPattern.contains(ORIG_FILE_PATTERN)) { + final String newPattern = ignoredPattern + (ignoredPattern.endsWith(";") ? "" : ";") + ORIG_FILE_PATTERN; + HgUtil.runWriteActionLater(new Runnable() { + public void run() { + FileTypeManager.getInstance().setIgnoredFilesList(newPattern); + } + }); + } + } + + @Override + public void deactivate() { + if (!started) { + return; + } + + StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); + if (messageBusConnection != null) { + messageBusConnection.disconnect(); + } + if (changesUpdaterScheduledFuture != null) { + changesUpdaterScheduledFuture.cancel(true); + } + if (statusBar != null) { + //statusBar.removeCustomIndicationComponent(incomingChangesStatus); + //statusBar.removeCustomIndicationComponent(outgoingChangesStatus); + //statusBar.removeCustomIndicationComponent(hgCurrentBranchStatus); + } + + if (myVFSListener != null) { + Disposer.dispose(myVFSListener); + myVFSListener = null; + } + + VirtualFileManager.getInstance().removeVirtualFileListener(myDirStateChangeListener); + } + + @Nullable + public static HgVcs getInstance(Project project) { + if (project == null || project.isDisposed()) { return null; } + final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project); + if (vcsManager == null) { return null; } + return (HgVcs) vcsManager.findVcsByName(VCS_NAME); + } + + private static String ourTestHgExecutablePath; // path to hg in test mode + + /** + * Sets the path to hg executable used in the test mode. + */ + public static void setTestHgExecutablePath(String path) { + ourTestHgExecutablePath = path; + } + + /** + * Returns the hg executable file. + * If it is a test, returns the special value set in the test setup. + * If it is a normal app, returns the value from global settings. + */ + public String getHgExecutable() { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return (new File(ourTestHgExecutablePath, HG_EXECUTABLE_FILE_NAME)).getPath(); + } + return globalSettings.getHgExecutable(); + } + + public HgGlobalSettings getGlobalSettings() { + return globalSettings; + } + + public void showMessageInConsole(String message, final TextAttributes style) { + myVcsManager.addMessageToConsoleWindow(message, style); + } + +} diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index a8a545b558e3..47f8e618d802 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1,1190 +1,1190 @@ - - com.intellij - IDEA CORE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - com.intellij.codeInsight.intention.impl.SplitIfAction - Control Flow - - - com.intellij.codeInsight.intention.impl.InvertIfConditionAction - Control Flow - - - com.intellij.codeInsight.daemon.impl.quickfix.RemoveRedundantElseAction - Control Flow - - - com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix - Control Flow - AddAnnotationFix - - - com.intellij.codeInsight.intention.impl.AddDeprecationAnnotationFix - Control Flow - AddAnnotationFix - - - com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix - Control Flow - AddAnnotationFix - - - com.intellij.codeInsight.intention.impl.DeannotateIntentionAction - Control Flow - - - - com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction - Declaration - - - com.intellij.codeInsight.intention.impl.AssignFieldFromParameterAction - Declaration - - - com.intellij.codeInsight.daemon.impl.quickfix.CreateLocalVarFromInstanceofAction - Declaration - - - com.intellij.codeInsight.daemon.impl.quickfix.CreateCastExpressionFromInstanceofAction - Declaration - - - com.intellij.codeInsight.intention.impl.CreateSubclassAction - Declaration - - - com.intellij.codeInsight.intention.impl.ImplementAbstractMethodAction - Declaration - - - com.intellij.codeInsight.intention.impl.CopyAbstractMethodImplementationAction - Declaration - - - com.intellij.codeInsight.intention.impl.SplitDeclarationAction - Declaration - - - com.intellij.codeInsight.intention.impl.MoveInitializerToConstructorAction - Declaration - - - com.intellij.codeInsight.intention.impl.MoveFieldAssignmentToInitializerAction - Declaration - - - com.intellij.codeInsight.daemon.impl.quickfix.AddRuntimeExceptionToThrowsAction - Declaration - - - com.intellij.codeInsight.intention.impl.MakeTypeGenericAction - Declaration - - - com.intellij.codeInsight.intention.impl.AddOverrideAnnotationAction - Declaration - - - - com.intellij.codeInsight.intention.impl.SimplifyBooleanExpressionAction - Boolean - - - - com.intellij.codeInsight.intention.impl.ConcatenationToMessageFormatAction - I18N - - - com.intellij.codeInsight.intention.impl.ConvertToBasicLatinAction - I18N - - - - com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction - Imports - - - com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction - Imports - - - - - com.intellij.codeInspection.actions.UnimplementInterfaceAction - Declaration - - - - com.intellij.codeInspection.actions.ReplaceImplementsWithStaticImportAction - Declaration - - - - com.intellij.codeInsight.intention.impl.ColorChooserIntentionAction - Declaration - - - - com.intellij.codeInsight.intention.impl.ConvertAbsolutePathToRelativeIntentionAction - Other - - - - com.intellij.codeInsight.intention.impl.ConvertRelativePathToAbsoluteIntentionAction - Other - - - - com.intellij.testIntegration.createTest.CreateTestAction - - - - com.intellij.codeInspection.concurrencyAnnotations.JCiPOrderEntryFix - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + com.intellij + IDEA CORE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.intellij.codeInsight.intention.impl.SplitIfAction + Control Flow + + + com.intellij.codeInsight.intention.impl.InvertIfConditionAction + Control Flow + + + com.intellij.codeInsight.daemon.impl.quickfix.RemoveRedundantElseAction + Control Flow + + + com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix + Control Flow + AddAnnotationFix + + + com.intellij.codeInsight.intention.impl.AddDeprecationAnnotationFix + Control Flow + AddAnnotationFix + + + com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix + Control Flow + AddAnnotationFix + + + com.intellij.codeInsight.intention.impl.DeannotateIntentionAction + Control Flow + + + + com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction + Declaration + + + com.intellij.codeInsight.intention.impl.AssignFieldFromParameterAction + Declaration + + + com.intellij.codeInsight.daemon.impl.quickfix.CreateLocalVarFromInstanceofAction + Declaration + + + com.intellij.codeInsight.daemon.impl.quickfix.CreateCastExpressionFromInstanceofAction + Declaration + + + com.intellij.codeInsight.intention.impl.CreateSubclassAction + Declaration + + + com.intellij.codeInsight.intention.impl.ImplementAbstractMethodAction + Declaration + + + com.intellij.codeInsight.intention.impl.CopyAbstractMethodImplementationAction + Declaration + + + com.intellij.codeInsight.intention.impl.SplitDeclarationAction + Declaration + + + com.intellij.codeInsight.intention.impl.MoveInitializerToConstructorAction + Declaration + + + com.intellij.codeInsight.intention.impl.MoveFieldAssignmentToInitializerAction + Declaration + + + com.intellij.codeInsight.daemon.impl.quickfix.AddRuntimeExceptionToThrowsAction + Declaration + + + com.intellij.codeInsight.intention.impl.MakeTypeGenericAction + Declaration + + + com.intellij.codeInsight.intention.impl.AddOverrideAnnotationAction + Declaration + + + + com.intellij.codeInsight.intention.impl.SimplifyBooleanExpressionAction + Boolean + + + + com.intellij.codeInsight.intention.impl.ConcatenationToMessageFormatAction + I18N + + + com.intellij.codeInsight.intention.impl.ConvertToBasicLatinAction + I18N + + + + com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction + Imports + + + com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction + Imports + + + + + com.intellij.codeInspection.actions.UnimplementInterfaceAction + Declaration + + + + com.intellij.codeInspection.actions.ReplaceImplementsWithStaticImportAction + Declaration + + + + com.intellij.codeInsight.intention.impl.ColorChooserIntentionAction + Declaration + + + + com.intellij.codeInsight.intention.impl.ConvertAbsolutePathToRelativeIntentionAction + Other + + + + com.intellij.codeInsight.intention.impl.ConvertRelativePathToAbsoluteIntentionAction + Other + + + + com.intellij.testIntegration.createTest.CreateTestAction + + + + com.intellij.codeInspection.concurrencyAnnotations.JCiPOrderEntryFix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a96f9bbe81c5b1fa75b6713a86df2c3379d7a38c Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Thu, 28 Oct 2010 14:57:11 +0400 Subject: [PATCH 22/36] editor detach: fixing laoding order --- .../intellij/ui/docking/DockContainer.java | 3 ++- .../impl/DockableEditorContainerFactory.java | 1 - .../impl/DockableEditorTabbedContainer.java | 20 +++++++++++++++++++ .../fileEditor/impl/EditorsSplitters.java | 2 ++ .../ui/docking/impl/DockManagerImpl.java | 4 ++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index 56b6809224b7..3b0b0309efe1 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -19,13 +19,14 @@ 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 { +public interface DockContainer extends Disposable, Activatable { RelativeRectangle getAcceptArea(); boolean canAccept(DockableContent content, RelativePoint point); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java index 47fcd60b97fe..2e98f3cf6d65 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java @@ -60,7 +60,6 @@ public class DockableEditorContainerFactory implements DockContainerFactory.Pers public DockContainer loadContainerFrom(Element element) { DockableEditorTabbedContainer container = (DockableEditorTabbedContainer)createContainer(); container.getSplitters().readExternal(element.getChild("state")); - container.getSplitters().openFiles(); return container; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index aaa63f1f19b0..353c8d9ce87a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -22,6 +22,7 @@ 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; @@ -51,6 +52,8 @@ class DockableEditorTabbedContainer implements DockContainer.Persistent { private boolean myDisposeWhenEmpty; private DockManager myDockManager; + private boolean myWasEverShown; + DockableEditorTabbedContainer(Project project, DockManager dockManager) { this(project, dockManager, null, true); } @@ -211,4 +214,21 @@ class DockableEditorTabbedContainer implements DockContainer.Persistent { public boolean isDisposeWhenEmpty() { return myDisposeWhenEmpty; } + + @Override + public void showNotify() { + if (!myWasEverShown) { + myWasEverShown = true; + IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() { + @Override + public void run() { + getSplitters().openFiles(); + } + }); + } + } + + @Override + public void hideNotify() { + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index ecefc2e1228b..d470ae45245e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -25,6 +25,7 @@ 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; @@ -80,6 +81,7 @@ public class EditorsSplitters extends JPanel { if (createOwnDockableContainer) { DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), dockManager, this, false); + Disposer.register(manager.getProject(), dockable); myDockManager.register(dockable); } } diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index 80cbd5b80d9f..0f1884210ea8 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -29,6 +29,7 @@ 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; @@ -389,6 +390,9 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo myContainer.closeAll(); } }); + + new UiNotifyConnector(frame.getContentPane(), myContainer); + return frame; } } From 706bdc31eaa16cc855043497ac44466a06f3cdb6 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Thu, 28 Oct 2010 15:44:13 +0400 Subject: [PATCH 23/36] IDEA-60205 Creating a java SDK on Mac does not include docs.jar --- .../com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java index 6b5d60f42471..cc478cc9dc29 100644 --- a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java @@ -235,6 +235,9 @@ public class JavaSdkImpl extends JavaSdk { VirtualFile commonDocs = findDocs(jdkHome, "docs"); if (commonDocs == null) { commonDocs = findInJar(new File(jdkHome, "docs.jar"), "doc/api"); + if (commonDocs == null) { + commonDocs = findInJar(new File(jdkHome, "docs.jar"), "docs/api"); + } } if (commonDocs != null) { sdkModificator.addRoot(commonDocs, JavadocOrderRootType.getInstance()); From f13d6d8c6e5c493df904f990788c80b2c9bcedbb Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Thu, 28 Oct 2010 15:58:39 +0400 Subject: [PATCH 24/36] IDEA-58068 Java Formatter: implement new spaces options: SPACE_BEFORE_ELSE/WHILE/CATCH/FINALLY_KEYWORD 1. Corresponding support is added; 2. Corresponding tests are added; --- .../java/JavaSpacePropertyProcessor.java | 14 ++- .../java/JavaFormatterSpaceTest.java | 98 +++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 33e563fc79cb..b6e09cc8b234 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -565,11 +565,15 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } else if (myRole1 == ChildRole.LOOP_BODY || myChild2.getElementType() == JavaElementType.CODE_BLOCK) { - processOnNewLineCondition(mySettings.WHILE_ON_NEW_LINE); + processOnNewLineCondition(mySettings.WHILE_ON_NEW_LINE, mySettings.SPACE_BEFORE_WHILE_KEYWORD); } } private void processOnNewLineCondition(final boolean onNewLine) { + processOnNewLineCondition(onNewLine, true); + } + + private void processOnNewLineCondition(final boolean onNewLine, final boolean createSpaceInline) { if (onNewLine) { if (!mySettings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE) { myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); @@ -580,7 +584,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } else { - createSpaceProperty(true, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); + createSpaceProperty(createSpaceInline, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } } @@ -597,7 +601,9 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { if (putRightChildOnNewLine) { processOnNewLineCondition(true); } else { - createSpaceProperty(true, false, 0); + boolean useSpace = (myRole2 == ChildRole.CATCH_SECTION && mySettings.SPACE_BEFORE_CATCH_KEYWORD) + || (myRole2 == ChildRole.FINALLY_KEYWORD && mySettings.SPACE_BEFORE_FINALLY_KEYWORD); + createSpaceProperty(useSpace, false, 0); } return; } @@ -747,7 +753,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } else { - createSpaceProperty(true, false, 0); + createSpaceProperty(mySettings.SPACE_BEFORE_ELSE_KEYWORD, false, 0); } } } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java index e19addbcf2cb..bebca173a534 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterSpaceTest.java @@ -176,4 +176,102 @@ public class JavaFormatterSpaceTest extends AbstractJavaFormatterTest { "int[] i2 = new int[]{1}" ); } + + public void testSpaceBeforeElse() throws Exception { + // Inspired by IDEA-58068 + getSettings().ELSE_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_ELSE_KEYWORD = false; + doMethodTest( + "if (true) {\n" + + "} else {\n" + + "}", + "if (true) {\n" + + "}else {\n" + + "}" + ); + + getSettings().SPACE_BEFORE_ELSE_KEYWORD = true; + doMethodTest( + "if (true) {\n" + + "}else {\n" + + "}", + "if (true) {\n" + + "} else {\n" + + "}" + ); + } + + public void testSpaceBeforeWhile() throws Exception { + // Inspired by IDEA-58068 + getSettings().WHILE_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_WHILE_KEYWORD = false; + doMethodTest( + "do {\n" + + "} while (true);", + "do {\n" + + "}while (true);" + ); + + getSettings().SPACE_BEFORE_WHILE_KEYWORD = true; + doMethodTest( + "do {\n" + + "}while (true);", + "do {\n" + + "} while (true);" + ); + } + + + public void testSpaceBeforeCatch() throws Exception { + // Inspired by IDEA-58068 + getSettings().CATCH_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_CATCH_KEYWORD = false; + doMethodTest( + "try {\n" + + "} catch (Exception e) {\n" + + "}", + "try {\n" + + "}catch (Exception e) {\n" + + "}" + ); + + getSettings().SPACE_BEFORE_CATCH_KEYWORD = true; + doMethodTest( + "try {\n" + + "}catch (Exception e) {\n" + + "}", + "try {\n" + + "} catch (Exception e) {\n" + + "}" + ); + } + + public void testSpaceBeforeFinally() throws Exception { + // Inspired by IDEA-58068 + getSettings().FINALLY_ON_NEW_LINE = false; + + getSettings().SPACE_BEFORE_FINALLY_KEYWORD = false; + doMethodTest( + "try {\n" + + "} finally {\n" + + "}", + "try {\n" + + "}finally {\n" + + "}" + ); + + getSettings().SPACE_BEFORE_FINALLY_KEYWORD = true; + doMethodTest( + "try {\n" + + "}finally {\n" + + "}", + "try {\n" + + "} finally {\n" + + "}" + ); + } + } From 6fabb8420181782950cf4ab7342be24697389711 Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Thu, 28 Oct 2010 15:58:59 +0400 Subject: [PATCH 25/36] Unnecessary property is removed --- .../src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java index eba4b435ffdc..698cd3ad5a7f 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java @@ -618,8 +618,6 @@ public class CommonCodeStyleSettings { //----------------- WRAPPING --------------------------- - public boolean WRAP_WHEN_TYPING_REACHES_RIGHT_MAGIN = false; - public static final int DO_NOT_WRAP = 0x00; public static final int WRAP_AS_NEEDED = 0x01; public static final int WRAP_ALWAYS = 0x02; From fb7a19c950115d1988332af412429ddc2d14f249 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Thu, 28 Oct 2010 16:25:09 +0400 Subject: [PATCH 26/36] EA-22844 read access --- .../src/org/jetbrains/android/facet/AndroidFacet.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java index 86274d86bee2..5c9520cd4884 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java @@ -386,8 +386,14 @@ public class AndroidFacet extends Facet { } @Nullable - public static AndroidFacet getInstance(@NotNull PsiElement element) { - Module module = ModuleUtil.findModuleForPsiElement(element); + public static AndroidFacet getInstance(@NotNull final PsiElement element) { + Module module = ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + @Override + public Module compute() { + return ModuleUtil.findModuleForPsiElement(element); + } + }); if (module == null) return null; return getInstance(module); } From fa63c096d0b8253a826795e48e11c38a7c60d9b9 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Thu, 28 Oct 2010 16:27:03 +0400 Subject: [PATCH 27/36] EA-22921 fix NPE --- .../android/src/org/jetbrains/android/run/DeviceChooser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java b/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java index 7cf92a12ebcd..9f3abbfd0130 100644 --- a/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java +++ b/plugins/android/src/org/jetbrains/android/run/DeviceChooser.java @@ -172,7 +172,8 @@ public class DeviceChooser extends DialogWrapper implements AndroidDebugBridge.I @NotNull private static String getDeviceState(@NotNull IDevice device) { - return capitalize(device.getState().name().toLowerCase()); + IDevice.DeviceState state = device.getState(); + return state != null ? capitalize(state.name().toLowerCase()) : ""; } @Override From 8cc2bb876b336ae13e3da774e316428d51ed86f5 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 28 Oct 2010 15:36:26 +0200 Subject: [PATCH 28/36] add "only weaken to an interface" option to "Type may be weakened" inspection --- .../siyeh/InspectionGadgetsBundle.properties | 1 + .../TypeMayBeWeakenedInspection.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index 2f3360ed75dc..84b735c31f7f 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1789,3 +1789,4 @@ log.method.name=Log method name log.condition.text=Log condition text ignore.if.annotated.by=Ignore if annotated by choose.annotation.class=Choose annotation class +only.weaken.to.an.interface=Only weaken to an interface diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java index 3b8105cedc8b..65ce2308d392 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/abstraction/TypeMayBeWeakenedInspection.java @@ -48,6 +48,9 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { @SuppressWarnings({"PublicField"}) public boolean doNotWeakenToJavaLangObject = true; + @SuppressWarnings({"PublicField"}) + public boolean onlyWeakentoInterface = true; + @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message( @@ -100,6 +103,8 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { optionsPanel.addCheckbox(InspectionGadgetsBundle.message( "type.may.be.weakened.do.not.weaken.to.object.option"), "doNotWeakenToJavaLangObject"); + optionsPanel.addCheckbox(InspectionGadgetsBundle.message("only.weaken.to.an.interface"), + "onlyWeakentoInterface"); return optionsPanel; } @@ -272,6 +277,15 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { variable.getResolveScope()); weakestClasses.remove(javaLangObjectClass); } + if (onlyWeakentoInterface) { + for (Iterator iterator = weakestClasses.iterator(); + iterator.hasNext();) { + final PsiClass weakestClass = iterator.next(); + if (!weakestClass.isInterface()) { + iterator.remove(); + } + } + } if (weakestClasses.isEmpty()) { return; } @@ -310,6 +324,15 @@ public class TypeMayBeWeakenedInspection extends BaseInspection { method.getResolveScope()); weakestClasses.remove(javaLangObjectClass); } + if (onlyWeakentoInterface) { + for (Iterator iterator = weakestClasses.iterator(); + iterator.hasNext();) { + final PsiClass weakestClass = iterator.next(); + if (!weakestClass.isInterface()) { + iterator.remove(); + } + } + } if (weakestClasses.isEmpty()) { return; } From 1a783e2596e56b6706301f95b9bbef3daf6f4d2d Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 28 Oct 2010 13:09:59 +0400 Subject: [PATCH 29/36] Unneeded instance creation eliminated --- java/openapi/src/com/intellij/lexer/JavaDocLexer.java | 2 +- java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/java/openapi/src/com/intellij/lexer/JavaDocLexer.java b/java/openapi/src/com/intellij/lexer/JavaDocLexer.java index 355e2432737b..6efb4ab16cce 100644 --- a/java/openapi/src/com/intellij/lexer/JavaDocLexer.java +++ b/java/openapi/src/com/intellij/lexer/JavaDocLexer.java @@ -20,6 +20,6 @@ package com.intellij.lexer; */ public class JavaDocLexer extends DocCommentLexer { public JavaDocLexer(final boolean isJdk15Enabled) { - super(new JavaDocTokenTypes(), isJdk15Enabled); + super(JavaDocTokenTypes.INSTANCE, isJdk15Enabled); } } diff --git a/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java b/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java index 732a16bba737..b99d7da89bd6 100644 --- a/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java +++ b/java/openapi/src/com/intellij/lexer/JavaDocTokenTypes.java @@ -22,6 +22,10 @@ import com.intellij.psi.tree.IElementType; * @author yole */ public class JavaDocTokenTypes implements DocCommentTokenTypes { + public static final DocCommentTokenTypes INSTANCE = new JavaDocTokenTypes(); + + private JavaDocTokenTypes() { } + public IElementType commentStart() { return JavaDocTokenType.DOC_COMMENT_START; } From b0dd089d6238140de0a7478ce0a62b41b40b6644 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 28 Oct 2010 17:55:54 +0400 Subject: [PATCH 30/36] Expression parsing fixed --- .../lang/java/parser/ExpressionParser.java | 29 ++++++++----------- .../expressions/ChainedClassObjectAccess.txt | 19 ++++++++++++ .../parser/partial/ExpressionParserTest.java | 1 + 3 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index 713bde2f8a4f..63471b258286 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -290,7 +290,7 @@ public class ExpressionParser { @Nullable private static PsiBuilder.Marker parsePostfix(final PsiBuilder builder) { - PsiBuilder.Marker operand = parsePrimary(builder, null); + PsiBuilder.Marker operand = parsePrimary(builder, null, -1); if (operand == null) return null; while (POSTFIX_OPS.contains(builder.getTokenType())) { @@ -306,7 +306,7 @@ public class ExpressionParser { private enum BreakPoint {P1, P2, P3, P4} @Nullable - private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final BreakPoint breakPoint) { + private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final BreakPoint breakPoint, final int breakOffset) { PsiBuilder.Marker startMarker = builder.mark(); PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder); @@ -323,19 +323,20 @@ public class ExpressionParser { final IElementType dotTokenType = builder.getTokenType(); if (dotTokenType == JavaTokenType.CLASS_KEYWORD && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { - if (breakPoint == BreakPoint.P1) { + if (breakPoint == BreakPoint.P1 && builder.getCurrentOffset() == breakOffset) { error(builder, JavaErrorMessages.message("expected.identifier")); PsiBuilderUtil.drop(startMarker, dotPos); return expr; } final PsiBuilder.Marker copy = startMarker.precede(); + final int offset = builder.getCurrentOffset(); startMarker.rollbackTo(); final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); - if (classObjAccess == null) { + if (classObjAccess == null || builder.getCurrentOffset() < offset) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P1); + return parsePrimary(builder, BreakPoint.P1, offset); } startMarker = copy; @@ -359,13 +360,13 @@ public class ExpressionParser { final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, false, true, false, false); if (ref == null || builder.getTokenType() != JavaTokenType.DOT) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P2); + return parsePrimary(builder, BreakPoint.P2, -1); } builder.advanceLexer(); if (builder.getTokenType() != dotTokenType) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P2); + return parsePrimary(builder, BreakPoint.P2, -1); } builder.advanceLexer(); @@ -422,7 +423,7 @@ public class ExpressionParser { } copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P3); + return parsePrimary(builder, BreakPoint.P3, -1); } else { startMarker.drop(); @@ -451,7 +452,7 @@ public class ExpressionParser { final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); if (classObjAccess == null || builder.getCurrentOffset() <= pos) { copy.rollbackTo(); - return parsePrimary(builder, BreakPoint.P4); + return parsePrimary(builder, BreakPoint.P4, -1); } startMarker = copy; @@ -828,16 +829,10 @@ public class ExpressionParser { if (type == JavaTokenType.GTGTGTEQ) { PsiBuilderUtil.advance(builder, 4); } - else if (type == JavaTokenType.GTGTGT) { + else if (type == JavaTokenType.GTGTGT || type == JavaTokenType.GTGTEQ) { PsiBuilderUtil.advance(builder, 3); } - else if (type == JavaTokenType.GTGTEQ) { - PsiBuilderUtil.advance(builder, 3); - } - else if (type == JavaTokenType.GTGT) { - PsiBuilderUtil.advance(builder, 2); - } - else if (type == JavaTokenType.GE) { + else if (type == JavaTokenType.GTGT || type == JavaTokenType.GE) { PsiBuilderUtil.advance(builder, 2); } else { diff --git a/java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt b/java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt new file mode 100644 index 000000000000..3fc7c2fc04fd --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/ChainedClassObjectAccess.txt @@ -0,0 +1,19 @@ +PsiJavaFile:ChainedClassObjectAccess.java + PsiReferenceExpression:A.class.B + PsiClassObjectAccessExpression:A.class + PsiTypeElement:A + PsiJavaCodeReferenceElement:A + PsiIdentifier:A('A') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiKeyword:class('class') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:B('B') + PsiJavaToken:DOT('.') + PsiErrorElement:Identifier expected + + PsiErrorElement:Unparsed tokens + PsiKeyword:class('class') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index db3af8cac07c..c5fb275f7855 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -105,6 +105,7 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testQualifiedSuperMethodCall1() { doParserTest("d.super(0)"); } public void testSuperMethodCallTypeParameterList() { doParserTest("super()"); } public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } + public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } private void doParserTest(final String text) { doParserTest(text, new TestParser() { From 0e9851fefb0139064f9ec51fcea10dc466f3ff3c Mon Sep 17 00:00:00 2001 From: anna Date: Thu, 28 Oct 2010 18:01:02 +0400 Subject: [PATCH 31/36] move caret to the end of line when delete self (IDEA-60486) --- .../IntroduceVariableBase.java | 19 ++++++++++++++++++- .../inplace/VariableInplaceRenamer.java | 18 +++++++----------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 18202cd2b853..1440f58bcd64 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -64,6 +64,8 @@ import com.intellij.refactoring.util.RefactoringUIUtil; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.occurences.ExpressionOccurenceManager; import com.intellij.refactoring.util.occurences.NotInSuperCallOccurenceFilter; +import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.*; import org.jetbrains.annotations.NonNls; @@ -477,7 +479,22 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme if (elementToRename != null) { editor.getCaretModel().moveToOffset(elementToRename.getTextOffset()); new VariableInplaceRenamer(elementToRename, editor) - .performInplaceRename(false, new LinkedHashSet(Arrays.asList(suggestedName.names)), exprMarker); + .performInplaceRename(false, new LinkedHashSet(Arrays.asList(suggestedName.names)), new Consumer(){ + @Override + public void consume(Boolean apply) { + if (apply) { + int startOffset = exprMarker.getStartOffset(); + final PsiReference referenceAt = file.findReferenceAt(startOffset); + if (referenceAt != null && referenceAt.resolve() instanceof PsiLocalVariable) { + startOffset = referenceAt.getElement().getTextRange().getEndOffset(); + } else { + startOffset = editor.getDocument().getLineEndOffset(editor.getDocument().getLineNumber(startOffset)); + } + editor.getCaretModel().moveToOffset(startOffset); + } + exprMarker.dispose(); + } + }); } } } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java index 6cc9cfe1281b..f03ed1b19187 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java @@ -59,6 +59,8 @@ import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.TextOccurrencesUtil; import com.intellij.usageView.UsageInfo; +import com.intellij.util.Consumer; +import com.intellij.util.Function; import com.intellij.util.PairProcessor; import com.intellij.util.containers.MultiMap; import com.intellij.util.containers.Stack; @@ -99,7 +101,7 @@ public class VariableInplaceRenamer { return performInplaceRename(true, null, null); } - public boolean performInplaceRename(boolean processTextOccurrences, LinkedHashSet nameSuggestions, final RangeMarker offsetAfter) { + public boolean performInplaceRename(boolean processTextOccurrences, LinkedHashSet nameSuggestions, final Consumer moveOffsetAfterr) { if (InjectedLanguageUtil.isInInjectedLanguagePrefixSuffix(myElementToRename)) { return false; } @@ -217,21 +219,15 @@ public class VariableInplaceRenamer { if (myNewName != null) { performAutomaticRename(myNewName, PsiTreeUtil.getParentOfType(containingFile.findElementAt(renameOffset), PsiNameIdentifierOwner.class)); } - if (offsetAfter != null) { - int startOffset = offsetAfter.getStartOffset(); - final PsiReference referenceAt = containingFile.findReferenceAt(startOffset); - if (referenceAt != null) { - startOffset = referenceAt.getElement().getTextRange().getEndOffset(); - } - myEditor.getCaretModel().moveToOffset(startOffset); - offsetAfter.dispose(); + if (moveOffsetAfterr != null) { + moveOffsetAfterr.consume(true); } } public void templateCancelled(Template template) { finish(); - if (offsetAfter != null) { - offsetAfter.dispose(); + if (moveOffsetAfterr != null) { + moveOffsetAfterr.consume(false); } } }); From 4e7b31609aced5c22555e10ed20f52920699e8ca Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Thu, 28 Oct 2010 18:10:06 +0400 Subject: [PATCH 32/36] NPE in tabs layout --- .../src/com/intellij/ui/tabs/impl/table/TablePassInfo.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java index 13073a83f7e9..0ce59e7855dc 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/table/TablePassInfo.java @@ -38,6 +38,7 @@ public class TablePassInfo extends LayoutPassInfo { TablePassInfo(final JBTabsImpl tabs, java.util.List visibleInfos) { super(visibleInfos); + myTabs = tabs; } public TabInfo getPreviousFor(final TabInfo info) { From 40d47c972b38b553daf01d9f8dd8062e7d920be1 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Thu, 28 Oct 2010 18:14:36 +0400 Subject: [PATCH 33/36] IDEA-60488 Leave the number of replacements done in the status bar longer --- .../codeInsight/daemon/impl/StatusBarUpdater.java | 2 +- .../src/com/intellij/openapi/wm/StatusBar.java | 8 ++++++-- .../src/com/intellij/openapi/wm/StatusBarInfo.java | 2 ++ .../intellij/openapi/wm/impl/TestWindowManager.java | 4 ++++ .../openapi/wm/impl/status/IdeStatusBarImpl.java | 7 ++++++- .../openapi/wm/impl/status/InfoAndProgressPanel.java | 11 ++++++++++- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java index dddcb961b6d8..a9325e08b8dc 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusBarUpdater.java @@ -90,7 +90,7 @@ public class StatusBarUpdater implements Disposable { if (statusBar instanceof StatusBarEx) { StatusBarEx barEx = (StatusBarEx)statusBar; if (!text.equals(barEx.getInfo())){ - statusBar.setInfo(text); + statusBar.setInfo(text, "updater"); } } } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java index d2697cb7331a..b774a96dec08 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java @@ -39,12 +39,16 @@ public interface StatusBar extends StatusBarInfo { } public static void set(@Nullable final String text, @Nullable final Project project) { + set(text, project, null); + } + + public static void set(@Nullable final String text, @Nullable final Project project, @Nullable final String requestor) { if (project != null) { if (project.isDisposed()) return; if (!project.isInitialized()) { StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { public void run() { - project.getMessageBus().syncPublisher(TOPIC).setInfo(text); + project.getMessageBus().syncPublisher(TOPIC).setInfo(text, requestor); } }); return; @@ -52,7 +56,7 @@ public interface StatusBar extends StatusBarInfo { } final MessageBus bus = project == null ? ApplicationManager.getApplication().getMessageBus() : project.getMessageBus(); - bus.syncPublisher(TOPIC).setInfo(text); + bus.syncPublisher(TOPIC).setInfo(text, requestor); } } diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java index 78d2ecc3cab9..7c4c371db1a5 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarInfo.java @@ -27,6 +27,8 @@ public interface StatusBarInfo { * @param s text to be shown in the status bar */ void setInfo(@Nullable String s); + + void setInfo(@Nullable String s, @Nullable String requestor); String getInfo(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java index 7773f8596b5a..16a4f059e9a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java @@ -74,6 +74,10 @@ public final class TestWindowManager extends WindowManagerEx implements Applicat return new Dimension(0, 0); } + @Override + public void setInfo(@Nullable String s, @Nullable String requestor) { + } + public boolean isVisible() { return false; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java index a48e302f3982..f12b2bf29612 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java @@ -280,10 +280,15 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx { } public void setInfo(@Nullable final String s) { + setInfo(s, null); + } + + @Override + public void setInfo(@Nullable final String s, @Nullable final String requestor) { UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { myInfo = s; - if (myInfoAndProgressPanel != null) myInfoAndProgressPanel.setText(s); + if (myInfoAndProgressPanel != null) myInfoAndProgressPanel.setText(s, requestor); } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java index cfa169289027..967852aca3f7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java @@ -72,6 +72,8 @@ public class InfoAndProgressPanel extends JPanel implements CustomStatusBarWidge private AsyncProcessIcon myRefreshIcon; private EmptyIcon myEmptyRefreshIcon; + private String myCurrentRequestor; + public InfoAndProgressPanel() { setOpaque(false); @@ -323,8 +325,15 @@ public class InfoAndProgressPanel extends JPanel implements CustomStatusBarWidge myRefreshAndInfoPanel.repaint(); } - public void setText(final String text) { + public void setText(final String text, final String requestor) { + if (text == null || text.length() == 0) { + if (requestor == null && myCurrentRequestor == null) return; + if (requestor != null && !requestor.equals(myCurrentRequestor)) return; + if (myCurrentRequestor != null && !myCurrentRequestor.equals(requestor)) return; + } + myInfoPanel.setText(text); + myCurrentRequestor = requestor; } public void setRefreshVisible(final boolean visible) { From 2ca0da6a6ed425243ccfe17497af667609e6f9a5 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Thu, 28 Oct 2010 18:19:07 +0400 Subject: [PATCH 34/36] Vertical scrollbar UI for editor is redesigned, again. --- .../editor/impl/EditorMarkupModelImpl.java | 46 ++++++------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 3c09eaa8d853..4c679ded4657 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -69,7 +69,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark private static final Icon ERRORS_FOUND_ICON = IconLoader.getIcon("/general/errorsFound.png"); private static final int ERROR_ICON_WIDTH = ERRORS_FOUND_ICON.getIconWidth(); private static final int ERROR_ICON_HEIGHT = ERRORS_FOUND_ICON.getIconHeight(); - private static final int PREFERRED_WIDTH = ERRORS_FOUND_ICON.getIconWidth() + 4; + private static final int PREFERRED_WIDTH = ERRORS_FOUND_ICON.getIconWidth() + 3; private final EditorImpl myEditor; private ErrorStripeRenderer myErrorStripeRenderer = null; private final List myErrorMarkerListeners = new ArrayList(); @@ -299,18 +299,15 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark final Rectangle bounds = getBounds(); - g.setColor(myEditor.getBackgroundColor()); + g.setColor(ButtonlessScrollBarUI.TRACK_BACKGROUND); g.fillRect(0, 0, bounds.width, bounds.height); - g.setColor(ButtonlessScrollBarUI.TRACK_BACKGROUND); - g.fillRect(3, 0, bounds.width, bounds.height); - g.setColor(ButtonlessScrollBarUI.TRACK_BORDER); - g.drawLine(3, 0, 3, bounds.height); + g.drawLine(0, 0, 0, bounds.height); try { if (myErrorStripeRenderer != null) { - myErrorStripeRenderer.paint(this, g, new Rectangle(5, 2, ERROR_ICON_WIDTH, ERROR_ICON_HEIGHT)); + myErrorStripeRenderer.paint(this, g, new Rectangle(3, 2, ERROR_ICON_WIDTH, ERROR_ICON_HEIGHT)); } } finally { @@ -354,15 +351,9 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - g.translate(-2, 0); - g.setColor(Color.white); - final Rectangle oldClip = g.getClipBounds(); - g.setClip(thumbBounds.x, thumbBounds.y, 5, thumbBounds.height ); - g.fillRect(thumbBounds.x + 2, thumbBounds.y + 2, thumbBounds.width, thumbBounds.height - 4); - - g.setClip(oldClip); + g.translate(5, 0); super.paintThumb(g, c, thumbBounds); - g.translate(2, 0); + g.translate(-5, 0); } @Override @@ -372,38 +363,27 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected int getThickness() { - return super.getThickness() + 5; + return super.getThickness() + 4; } @Override protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { - Rectangle bounds = new Rectangle(trackBounds); - - g.setColor(myEditor.getBackgroundColor()); - g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); - - bounds.width /= 1.20; - final int shift = trackBounds.width - bounds.width; - - g.translate(shift, 0); - - super.paintTrack(g, c, bounds); + super.paintTrack(g, c, trackBounds); ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); try { Rectangle clipBounds = g.getClipBounds(); - repaint(g, ERROR_ICON_WIDTH - 2, clipBounds); + repaint(g, ERROR_ICON_WIDTH - 1, clipBounds); } finally { - g.translate(-shift, 0); ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintFinish(); } } @Override protected Color adjustColor(Color c) { - return ColorUtil.withAlpha(super.adjustColor(c), 0.85); + return ColorUtil.withAlpha(ColorUtil.shift(super.adjustColor(c), 0.9), 0.85); } private void repaint(final Graphics g, final int width, Rectangle clipBounds) { @@ -505,7 +485,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark int i = stripes.indexOf(endingStripe); stripes.remove(i); if (i == 0) { - // visible + // visible f drawSpot(g, width, endingStripe.thin, yStart, endingStripe.yEnd, endingStripe.color, true, true); yStart = endingStripe.yEnd; } @@ -521,12 +501,12 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark Color color, boolean drawTopDecoration, boolean drawBottomDecoration) { - int x = 3; + int x = 4; int paintWidth = width; if (thinErrorStripeMark) { paintWidth /= 2; paintWidth += 1; - x += paintWidth + 1; + x = 0; } if (color == null) return; g.setColor(color); From 0640fe409348cdbc1f5753711181cf4006119de9 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 28 Oct 2010 18:32:56 +0400 Subject: [PATCH 35/36] IDEA-60494 --- .../src/com/intellij/psi/impl/source/tree/JavaElementType.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 8f1dd139c86d..4ce85e1d9090 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -41,6 +41,8 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.CharTable; import com.intellij.util.diff.FlyweightCapableTreeStructure; +import static com.intellij.lang.PsiBuilderUtil.expect; + public interface JavaElementType { IElementType CLASS = JavaStubElementTypes.CLASS; IElementType ANONYMOUS_CLASS = JavaStubElementTypes.ANONYMOUS_CLASS; @@ -252,6 +254,7 @@ public interface JavaElementType { new JavaParserUtil.ParserWrapper() { public void parse(final PsiBuilder builder) { ReferenceParser.parseType(builder); + expect(builder, JavaTokenType.ELLIPSIS); } }); } From a0d0b5c2b8a1d72d143aec5704676aa152e3a8dd Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 28 Oct 2010 17:26:56 +0200 Subject: [PATCH 36/36] IDEA-58633 ("Declaration Redundancy" inspection misses implicit constructor super() calls) --- .../com/intellij/codeInspection/reference/RefMethodImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java b/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java index 188c0ef7e06a..babae2d55058 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java +++ b/java/java-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java @@ -306,7 +306,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { final PsiCodeBlock body = method.getBody(); if (body == null) return; - final Collection exceptionTypes = ExceptionUtil.collectUnhandledExceptions(method, body); + final Collection exceptionTypes = ExceptionUtil.collectUnhandledExceptions(body, method); for (final PsiClassType exceptionType : exceptionTypes) { updateThrowsList(exceptionType); }