diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/NextProjectWindow.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/NextProjectWindow.java
new file mode 100644
index 000000000000..f6b7b5f2fbf3
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/NextProjectWindow.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2011 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.wm.impl;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAware;
+
+public class NextProjectWindow extends AnAction implements DumbAware {
+
+ public void actionPerformed(AnActionEvent e) {
+ WindowDressing.getWindowActionGroup().activateNextWindow(e);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(WindowDressing.getWindowActionGroup().isEnabled());
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/PreviousProjectWindow.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/PreviousProjectWindow.java
new file mode 100644
index 000000000000..1776a5362334
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/PreviousProjectWindow.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2011 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.wm.impl;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.DumbAware;
+
+public class PreviousProjectWindow extends AnAction implements DumbAware {
+
+ public void actionPerformed(AnActionEvent e) {
+ WindowDressing.getWindowActionGroup().activatePreviousWindow(e);
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(WindowDressing.getWindowActionGroup().isEnabled());
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectWindowAction.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectWindowAction.java
new file mode 100644
index 000000000000..8a1d4a925317
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectWindowAction.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2011 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.wm.impl;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.ToggleAction;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.wm.WindowManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+
+/**
+ * This class is programmatically instantiated and registered when opening and closing projects
+ * and thus not registered in plugin.xml
+ */
+@SuppressWarnings({"ComponentNotRegistered"})
+public class ProjectWindowAction extends ToggleAction implements DumbAware {
+
+ private ProjectWindowAction previous;
+ private ProjectWindowAction next;
+
+ public ProjectWindowAction(@NotNull String projectName, ProjectWindowAction previous) {
+ super(projectName);
+ if (previous != null) {
+ this.previous = previous;
+ this.next = previous.next;
+ previous.next = this;
+ if (previous.previous == previous) {
+ previous.previous = this;
+ }
+ } else {
+ this.previous = this;
+ this.next = this;
+ }
+ }
+
+ public void dispose() {
+ if (previous == this) {
+ assert next == this;
+ return;
+ }
+ if (next == this) {
+ assert false;
+ return;
+ }
+ previous.next = next;
+ next.previous = previous;
+ }
+
+ public ProjectWindowAction getPrevious() {
+ return previous;
+ }
+
+ public ProjectWindowAction getNext() {
+ return next;
+ }
+
+ @Nullable
+ public static Frame findProjectFrame(@NotNull String projectName) {
+ final Project[] projects = ProjectManager.getInstance().getOpenProjects();
+ for (Project project : projects) {
+ if (projectName.equals(project.getName())) {
+ final WindowManager windowManager = WindowManager.getInstance();
+ return windowManager.getFrame(project);
+ }
+ }
+ return null;
+ }
+
+ public boolean isSelected(AnActionEvent e) {
+ // show check mark for active and visible project frame
+ final Project project = e.getData(PlatformDataKeys.PROJECT);
+ if (project == null) {
+ return false;
+ }
+ final String text = getTemplatePresentation().getText();
+ return text.equals(project.getName());
+ }
+
+ public void setSelected(@Nullable AnActionEvent e, boolean selected) {
+ if (!selected) {
+ return;
+ }
+ final Frame projectFrame = findProjectFrame(getTemplatePresentation().getText());
+ if (projectFrame == null) {
+ return;
+ }
+ final int frameState = projectFrame.getExtendedState();
+ if ((frameState & Frame.ICONIFIED) == Frame.ICONIFIED) {
+ // restore the frame if it is minimized
+ projectFrame.setExtendedState(frameState ^ Frame.ICONIFIED);
+ }
+ // bring the frame forward
+ projectFrame.toFront();
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectWindowActionGroup.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectWindowActionGroup.java
new file mode 100644
index 000000000000..5fd281d23298
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectWindowActionGroup.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2000-2011 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.wm.impl;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class ProjectWindowActionGroup extends DefaultActionGroup {
+
+ private ProjectWindowAction latest = null;
+
+ public void addProject(@NotNull String name) {
+ final ProjectWindowAction windowAction = new ProjectWindowAction(name, latest);
+ add(windowAction);
+ latest = windowAction;
+ }
+
+ public void removeProject(@NotNull String name) {
+ final ProjectWindowAction windowAction = findWindowAction(name);
+ if (windowAction == null) {
+ return;
+ }
+ if (latest == windowAction) {
+ final ProjectWindowAction previous = latest.getPrevious();
+ if (previous != latest) {
+ latest = previous;
+ } else {
+ latest = null;
+ }
+ }
+ windowAction.dispose();
+ remove(windowAction);
+ }
+
+ public boolean isEnabled() {
+ return latest.getPrevious() != latest;
+ }
+
+ @Override
+ public boolean isDumbAware() {
+ return true;
+ }
+
+ public void activateNextWindow(AnActionEvent e) {
+ final Project project = e.getData(PlatformDataKeys.PROJECT);
+ if (project == null) {
+ return;
+ }
+ final ProjectWindowAction windowAction = findWindowAction(project.getName());
+ final ProjectWindowAction next = windowAction.getNext();
+ if (next != null) {
+ next.setSelected(e, true);
+ }
+ }
+
+ public void activatePreviousWindow(AnActionEvent e) {
+ final Project project = e.getData(PlatformDataKeys.PROJECT);
+ if (project == null) {
+ return;
+ }
+ final ProjectWindowAction windowAction = findWindowAction(project.getName());
+ final ProjectWindowAction previous = windowAction.getPrevious();
+ if (previous != null) {
+ previous.setSelected(e, true);
+ }
+ }
+
+ private ProjectWindowAction findWindowAction(String name) {
+ final AnAction[] children = getChildren(null);
+ for (AnAction child : children) {
+ if (!(child instanceof ProjectWindowAction)) {
+ continue;
+ }
+ final ProjectWindowAction windowAction = (ProjectWindowAction) child;
+ if (name.equals(windowAction.getTemplatePresentation().getText())) {
+ return windowAction;
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowDressing.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowDressing.java
new file mode 100644
index 000000000000..977a447ea85e
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowDressing.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2011 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.wm.impl;
+
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.components.AbstractProjectComponent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class WindowDressing extends AbstractProjectComponent {
+
+ public WindowDressing(@NotNull Project project) {
+ super(project);
+ }
+
+ public void projectOpened() {
+ getWindowActionGroup().addProject(myProject.getName());
+ }
+
+ public void projectClosed() {
+ getWindowActionGroup().removeProject(myProject.getName());
+ }
+
+ public static ProjectWindowActionGroup getWindowActionGroup() {
+ return (ProjectWindowActionGroup)ActionManager.getInstance().getAction("OpenProjectWindows");
+ }
+}
diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties
index e9c5f72d240e..e7b61fd46c6c 100644
--- a/platform/platform-resources-en/src/messages/ActionsBundle.properties
+++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties
@@ -785,6 +785,11 @@ action.NextSplitter.text=_Goto Next Splitter
action.NextSplitter.description=Goto next splitter window
action.PrevSplitter.text=Goto Previous Splitter
action.PrevSplitter.description=Goto previous splitter window
+action.NextProjectWindow.text=Next Project Window
+action.NextProjectWindow.description=Switch to the next project window
+action.PreviousProjectWindow.text=Previous Project Window
+action.PreviousProjectWindow.description=Switch to the previous project window
+group.OpenProjectWindows.text=Open Project Windows
group.HelpMenu.text=_Help
action.HelpTopics.text=Help _Topics
action.HelpTopics.description=Show help contents
diff --git a/platform/platform-resources/src/componentSets/WindowManagement.xml b/platform/platform-resources/src/componentSets/WindowManagement.xml
index 6a4b4a9b85e0..401a8a12772e 100644
--- a/platform/platform-resources/src/componentSets/WindowManagement.xml
+++ b/platform/platform-resources/src/componentSets/WindowManagement.xml
@@ -19,5 +19,9 @@
com.intellij.openapi.wm.impl.IdeFocusManagerImpl
com.intellij.openapi.wm.impl.IdeFocusManagerHeadless
+
+ com.intellij.openapi.wm.impl.WindowDressing
+
+
\ No newline at end of file
diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml
index 41215f4027df..e4ca0f809134 100644
--- a/platform/platform-resources/src/idea/Keymap_Default.xml
+++ b/platform/platform-resources/src/idea/Keymap_Default.xml
@@ -894,6 +894,12 @@
+
+
+
+
+
+